Spring 网关和 Auth0:IllegalArgumentException:无法找到名为 TokenRelay 的 GatewayFilterFactory

Spring Gateway and Auth0: IllegalArgumentException: Unable to find GatewayFilterFactory with name TokenRelay

我正在尝试构建一个 spring 网关,它正在获取 JWT 并将令牌发送到所有底层服务。为此,我使用以下依赖项:

<!-- Spring Boot Dependencies -->
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-validation</artifactId>
</dependency>
<dependency>
    <groupId>org.springframework.security</groupId>
    <artifactId>spring-security-oauth2-jose</artifactId>
</dependency>
<!-- Spring Boot Dependencies -->

<!-- Spring Cloud Dependencies -->
<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-gateway</artifactId>
</dependency>
<!-- Spring Cloud Dependencies -->

我为 Auth0 配置了我的应用程序:

spring:
  cloud:
    gateway:
      routes:
        - id: my-service
          uri: http://localhost:8001/
          predicates:
            - Path=/comments
          filters:
            - TokenRelay=   #to send the token to the underlying service
            - RemoveRequestHeader=Cookie    #remove cookies since underlying services don't need them
  security:
    oauth2:
      resourceserver:
        jwt:
          issuer-uri: #my issuer-uri
          audience: #my audience

我像描述的那样实现了受众验证器和 jwt 解码器 here:

@Configuration
@ConditionalOnProperty(name = {"spring.security.oauth2.resourceserver.jwt.issuer-uri"})
public class AuthenticationOauth2Configuration {

    @Value("${spring.security.oauth2.resourceserver.jwt.audience}")
    private String audience;

    @Value("${spring.security.oauth2.resourceserver.jwt.issuer-uri}")
    private String issuer;

    @Bean(name = "customJwtDecoder")
    public JwtDecoder getJwtDecoder() {
        final NimbusJwtDecoder jwtDecoder = (NimbusJwtDecoder) JwtDecoders.fromOidcIssuerLocation(issuer);
        final OAuth2TokenValidator<Jwt> audienceValidator = new JwtAudienceValidator(audience);
        final OAuth2TokenValidator<Jwt> issuer = JwtValidators.createDefaultWithIssuer(this.issuer);
        final OAuth2TokenValidator<Jwt> audience = new DelegatingOAuth2TokenValidator<>(issuer, audienceValidator);

        jwtDecoder.setJwtValidator(audience);

        return jwtDecoder;
    }
}


public class JwtAudienceValidator implements OAuth2TokenValidator<Jwt> {

    private final String audience;

    public JwtAudienceValidator(final String audience) {
        this.audience = audience;
    }

    @Override
    public OAuth2TokenValidatorResult validate(Jwt jwt) {
        final OAuth2Error error = new OAuth2Error("invalid_token", "The required audience is missing", null);

        if (jwt.getAudience().contains(audience)) {
            return OAuth2TokenValidatorResult.success();
        }

        return OAuth2TokenValidatorResult.failure(error);
    }
}

然而,当我启动网关服务时,出现以下错误:

Caused by: reactor.core.Exceptions$ErrorCallbackNotImplemented: java.lang.IllegalArgumentException: Unable to find GatewayFilterFactory with name TokenRelay
Caused by: java.lang.IllegalArgumentException: Unable to find GatewayFilterFactory with name TokenRelay

我实在找不到任何关于如何解决这个问题的资源。

您需要 org.springframework.boot:spring-boot-starter-oauth2-client,如前所述 here。 但是我不认为你一使用资源服务器就需要它。网关将在没有任何配置的情况下将您的 header 转发到下游,因此您将能够在那里找到授权 header。

举个 Eduard Khachirov 所说的例子:

依赖项:

<dependency>
    <groupId>org.springframework.security</groupId>
    <artifactId>spring-security-oauth2-resource-server</artifactId>
</dependency>
<dependency>
    <groupId>org.springframework.security</groupId>
    <artifactId>spring-security-oauth2-jose</artifactId>
</dependency>

服务application.yml:

spring:
  security:
    oauth2:
      resourceserver:
        jwt:
          issuer-uri: https://<AUTH0_DOMAIN>/

auth0:
  audience: <AUTH0_API_AUDIENCE>

服务安全配置:

@Configuration
@EnableWebSecurity
public class Oauth2ResourceServerConfiguration extends ResourceServerConfigurerAdapter {

    @Value("${auth0.audience}")
    private String audience;

    @Value("${spring.security.oauth2.resourceserver.jwt.issuer-uri}")
    private String issuer;

    @Override
    public void configure(final HttpSecurity http) throws Exception {
        http
                .authorizeRequests()
                .anyRequest().authenticated()
                .and()
                .oauth2ResourceServer()
                .jwt();
    }

    @Bean
    public JwtDecoder jwtDecoder() {
        final NimbusJwtDecoder jwtDecoder = (NimbusJwtDecoder) JwtDecoders.fromOidcIssuerLocation(issuer);

        jwtDecoder.setJwtValidator(new DelegatingOAuth2TokenValidator<>(
                JwtValidators.createDefaultWithIssuer(issuer),
                new AudienceValidator(audience)));

        return jwtDecoder;
    }

    static class AudienceValidator implements OAuth2TokenValidator<Jwt> {
        private final String audience;

        public AudienceValidator(final String audience) {
            this.audience = audience;
        }

        public OAuth2TokenValidatorResult validate(final Jwt jwt) {
            if (jwt.getAudience().contains(audience)) {
                return OAuth2TokenValidatorResult.success();
            }

            return OAuth2TokenValidatorResult.failure(new OAuth2Error("invalid_token", "The required audience is missing", null));
        }
    }
}

网关application.yml

spring:
  security:
    oauth2:
      resourceserver:
        jwt:
          issuer-uri: https://<AUTH0_DOMAIN>/
  cloud:
    gateway:
      routes:
        - id: my-service
          uri: lb://MY-SERVICE
          predicates:
            - Path=/api
    loadbalancer:
      ribbon:
        enabled: false

网关安全配置:

@Configuration
@EnableWebFluxSecurity
public class Oauth2ResourceServerConfiguration extends ResourceServerConfigurerAdapter {

    @Override
    public void configure(final HttpSecurity http) throws Exception {
        http
                .authorizeRequests()
                .anyRequest().authenticated()
                .and()
                .oauth2ResourceServer()
                .jwt();
    }
}

我有同样的问题,我需要一个 OAuth2 消费者充当客户端并将传入的令牌转发到传出的资源请求。

因为我使用的是 Spring Cloud Gateway 嵌入式反向代理,所以我可以要求它向下游转发 OAuth2 访问令牌到它正在代理的服务。因此,上面的 SSO 应用程序可以像这样简单地增强(使用 TokenRelay 过滤器):

spring:
  cloud:
    gateway:
      routes:
      - id: resource
        uri: http://localhost:9000
        predicates:
        - Path=/resource
        filters:
        - TokenRelay=

要为 Spring Cloud Gateway 启用此功能,请添加以下依赖项

  • org.springframework.boot:spring-boot-starter-oauth2-client
  • org.springframework.cloud:spring-cloud-starter-security.

我有这个 pom.xml 配置:

<dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-oauth2-client</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-security</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-gateway</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springdoc</groupId>
            <artifactId>springdoc-openapi-webflux-core</artifactId>
            <version>${springdoc.openapi.webflux}</version>
        </dependency>
        <dependency>
            <groupId>org.springdoc</groupId>
            <artifactId>springdoc-openapi-webflux-ui</artifactId>
            <version>${springdoc.openapi.webflux}</version>
        </dependency>
    </dependencies>

为了 spring 云网关将令牌传递给下游服务并验证令牌,您需要以下依赖项:

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-oauth2-client</artifactId>
</dependency>
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-oauth2-resource-server</artifactId>
</dependency>