Spring 云 API 网关自定义筛选器 Object 上的 ClassCastException 以配置

Spring Cloud API Gateway Custom Filters ClassCastException on Object to Config

我正在尝试通过关注此资源 spring-cloud-gateway-creating-custom-route-filters-abstractgatewayfilterfactory.

在我的 Spring 云 API 网关中为授权 Header 实施自定义过滤器

这是我的设置。

pom.xml

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
  <modelVersion>4.0.0</modelVersion>
  <parent>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-parent</artifactId>
    <version>2.3.6.RELEASE</version>
    <relativePath/> <!-- lookup parent from repository -->
  </parent>
  <groupId>com.myapp.config</groupId>
  <artifactId>MyAPIGateway</artifactId>
  <version>0.0.1-SNAPSHOT</version>
  <name>MyAPIGateway</name>
  <description>Spring Cloud API Gateway Server</description>
  <properties>
    <java.version>11</java.version>
    <spring-cloud.version>Hoxton.SR9</spring-cloud.version>
  </properties>
  <dependencies>

    <dependency>
      <groupId>org.springframework.cloud</groupId>
      <artifactId>spring-cloud-starter-gateway</artifactId>
    </dependency>
    <dependency>
      <groupId>org.springframework.cloud</groupId>
      <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
    </dependency>

    <dependency>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-starter-webflux</artifactId>
    </dependency>
    <dependency>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-starter-test</artifactId>
      <scope>test</scope>
    </dependency>

    <dependency>
      <groupId>io.jsonwebtoken</groupId>
      <artifactId>jjwt</artifactId>
      <version>0.9.1</version>
    </dependency>

    <dependency>
      <groupId>org.projectlombok</groupId>
      <artifactId>lombok</artifactId>
      <optional>true</optional>
    </dependency>
    <dependency>
      <groupId>javax.xml.bind</groupId>
      <artifactId>jaxb-api</artifactId>
    </dependency>

    <dependency>
      <groupId>io.projectreactor</groupId>
      <artifactId>reactor-test</artifactId>
      <scope>test</scope>
    </dependency>
    <dependency>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-autoconfigure</artifactId>
      <version>2.3.6.RELEASE</version>
      <scope>compile</scope>
    </dependency>
    <dependency>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot</artifactId>
      <version>2.3.6.RELEASE</version>
      <scope>compile</scope>
    </dependency>
  </dependencies>
  <dependencyManagement>
    <dependencies>
      <dependency>
        <groupId>org.springframework.cloud</groupId>
        <artifactId>spring-cloud-dependencies</artifactId>
        <version>${spring-cloud.version}</version>
        <type>pom</type>
        <scope>import</scope>
      </dependency>
    </dependencies>
  </dependencyManagement>

  <build>
    <plugins>
      <plugin>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-maven-plugin</artifactId>
        <configuration>
          <excludes>
            <exclude>
              <groupId>org.projectlombok</groupId>
              <artifactId>lombok</artifactId>
            </exclude>
          </excludes>
        </configuration>
      </plugin>
    </plugins>
  </build>

</project>

application.yml(我用过滤器的地方)

routes:
        - id: user-registration
          uri: lb://users
          predicates:
            - Path=/users/v1/registration
            - Method=POST
            - Header=Authorization, Bearer (.*)
          filters:
            - RewritePath=/users-ws/(?<segment>.*), /$\{segment}
            - AuthorizationHeaderFilter

授权Header过滤器

@Component
public class AuthorizationHeaderFilter extends AbstractGatewayFilterFactory<AuthorizationHeaderFilter.Config> {

  private JwtSecretKey jwtSecretKey;
  private Environment environment;

  public AuthorizationHeaderFilter() {
    super(Config.class);
  }

  public static class Config {

    private String name;
    private String message;

    public String getName() {
      return name;
    }

    public void setName(String name) {
      this.name = name;
    }

    public String getMessage() {
      return message;
    }

    public void setMessage(String message) {
      this.message = message;
    }

  }

  @Autowired
  public AuthorizationHeaderFilter(JwtSecretKey jwtSecretKey, Environment environment) {
    this.jwtSecretKey = jwtSecretKey;
    this.environment = environment;
  }

  @Override
  public GatewayFilter apply(Config config) {
    return ((exchange, chain) -> {
      ServerHttpRequest request = exchange.getRequest();
      if (!request.getHeaders().containsKey(HttpHeaders.AUTHORIZATION)) {
        return onError(exchange, "Not Authorized!");
      }

      String authorizationHeader = request.getHeaders().get(HttpHeaders.AUTHORIZATION).get(0);
      String token = authorizationHeader.replace("Bearer ", "");

      try {
        if (!isJwtValid(token)) {
          return onError(exchange, "Request Not Authorized!");
        }
      } catch (InvalidKeySpecException | NoSuchAlgorithmException e) {
        e.printStackTrace();
      }

      return chain.filter(exchange);
    });
  }

  private Mono<Void> onError(ServerWebExchange exchange, String message) {
    ServerHttpResponse response = exchange.getResponse();
    response.setStatusCode(HttpStatus.UNAUTHORIZED);
    return response.setComplete();
  }

  private boolean isJwtValid(String token) throws InvalidKeySpecException, NoSuchAlgorithmException {
    boolean result = true;

    Jws<Claims> claimsJws = Jwts
      .parser()
      .setSigningKey(jwtSecretKey.getVerifyingKey())
      .parseClaimsJws(token);

    Claims body = claimsJws.getBody();
    String subject = body.getSubject();
    String issuer = body.getIssuer();

    if (subject == null || subject.isEmpty()) {
      result = false;
    } else if (!issuer.equals(environment.getProperty("jwt.issuer"))) {
      result = false;
    }

    return result;
  }

}

我无法弄清楚 Config class 应该包含什么(我试过空)并得到这个来自控制台的错误

Refresh routes error !!!

reactor.core.Exceptions$ErrorCallbackNotImplemented: java.lang.ClassCastException: class java.lang.Object cannot be cast to class com.myapp.config.gateway.AuthorizationHeaderFilter$Config (java.lang.Object is in module java.base of loader 'bootstrap'; com.myapp.config.gateway.AuthorizationHeaderFilter$Config is in unnamed module of loader 'app')
Caused by: java.lang.ClassCastException: class java.lang.Object cannot be cast to class com.myapp.config.gateway.AuthorizationHeaderFilter$Config (java.lang.Object is in module java.base of loader 'bootstrap'; com.myapp.config.gateway.AuthorizationHeaderFilter$Config is in unnamed module of loader 'app')
    at com.myapp.config.gateway.AuthorizationHeaderFilter.apply(AuthorizationHeaderFilter.java:27) ~[classes/:na]

我认为问题是因为你有 2 个构造函数,1 个是空的,1 个是自动装配的。尝试删除空构造函数并将 super 方法移动到第二个构造函数中,如下所示。

  @Autowired
  public AuthorizationHeaderFilter(JwtSecretKey jwtSecretKey, Environment environment) {
    this.jwtSecretKey = jwtSecretKey;
    this.environment = environment;
    super(Config.class);
  }