Reputation: 10589
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.
Upvotes: 0
Views: 6201
Reputation: 148
Mislocating your folders could be another reason for this error. Make sure you didn't place your filters out of the com.yourcompany.yourproejct
.
Upvotes: 0
Reputation: 1249
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>
Upvotes: 2
Reputation: 1824
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
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>
Upvotes: 0
Reputation: 10589
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();
}
}
Upvotes: 0
Reputation: 118
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.
Upvotes: 1