简体   繁体   中英

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

Im trying to build a spring gateway which is getting JWT and is sending the tokens to all underlying services. For this I use the following dependencies:

<!-- 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 -->

I configured my application for 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

I implemented the audience validator and the jwt decoder like described 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);
    }
}

However when Im starting the gateway service im getting the following 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

I literally cant find any resources on how to fix this.

You need org.springframework.boot:spring-boot-starter-oauth2-client as said here . But I don't think you need it as soon as you use resource server. Gateway will forward your headers downstream without any configuration, so you will be able to find the authorization header there.

To give an example to what Eduard Khachirov said:

dependencies:

<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>

service application.yml:

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

auth0:
  audience: <AUTH0_API_AUDIENCE>

service security config:

@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));
        }
    }
}

gateway 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

gateway security config:

@Configuration
@EnableWebFluxSecurity
public class Oauth2ResourceServerConfiguration extends ResourceServerConfigurerAdapter {

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

I had the same problem, I needed an OAuth2 consumer acts as a Client and forwards the incoming token to outgoing resource requests.

As I were using a Spring Cloud Gateway embedded reverse proxy then I could ask it to forward OAuth2 access tokens downstream to the services it is proxying. Thus the SSO app above can be enhanced simply like this (using TokenRelay Filter):

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

To enable this for Spring Cloud Gateway add the following dependencies

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

I had this pom.xml configuration:

<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>

In order for spring cloud gateway to pass tokens to downstream services and validate tokens you need the following dependencies:

<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>

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM