Reputation: 13
I am using spring boot 3.0.2 and spring cloud gateway. also, I using spring-boot-starter-oauth2-resource-server to obtain and check jwt token on Keycloak server for security. my build.gradle file like this:
plugins {
id 'java'
id 'org.springframework.boot' version '3.0.2'
id 'io.spring.dependency-management' version '1.1.3'
}
dependencies {
implementation 'org.springframework.cloud:spring-cloud-starter'
implementation 'org.springframework.boot:spring-boot-starter-webflux'
implementation 'org.springframework.cloud:spring-cloud-starter-gateway'
implementation 'org.springframework.boot:spring-boot-starter-data-jpa'
implementation 'org.springframework.boot:spring-boot-starter-data-mongodb'
implementation 'org.springframework.boot:spring-boot-starter-oauth2-resource-server'
implementation 'org.springframework.boot:spring-boot-starter-actuator'
implementation 'com.shahrtech.service.libs:service-common-libs:0.0.61'
implementation 'org.springframework.cloud:spring-cloud-starter-circuitbreaker-reactor-resilience4j'
implementation 'org.springframework.cloud:spring-cloud-starter-loadbalancer'
}
and config file on application.yml is :
spring :
cloud:
gateway:
default-filters:
- DedupeResponseHeader=Access-Control-Allow-Credentials Access-Control-Allow- Origin
globalcors:
corsConfigurations:
'[/**]':
allowedOrigins: "*"
allowedMethods: "*"
allowedHeaders: "*"
routes:
- id: auth-service
uri: http://x.x.x.x:8085/
predicates:
- Path=/auth-service/v1.0/**
filters:
- RewritePath=/auth-service/v1.0/(?<path>.*),/auth-service/v1.0/$\{path}
- RequestHashing=SHA-256
- id: customer-service
uri: http://x.x.x.x:8090/
predicates:
- Path=/customer-service/v1.0/**
filters:
- RewritePath=/customer-service/v1.0/(?<path>.*),/customer-service/v1.0/$\{path}
- RequestHashing=SHA-256
- TokenRelay=
security:
oauth2:
client:
registration:
keycloak:
client-id: login-app
authorization-grant-type: authorization_code
scope: openid
provider:
keycloak:
issuer-uri: http://x.x.x.x:9002/auth/realms/app
user-name-attribute: preferred_username
resourceserver:
jwt:
issuer-uri: http://x.x.x.x:9002/auth/realms/app
and ApiGatewayServiceApplication.java is :
@SpringBootApplication
@EnableDiscoveryClient
@EnableWebFluxSecurity
@EnableReactiveMethodSecurity
public class ApiGatewayServiceApplication {
@Bean
public SecurityWebFilterChain securityWebFilterChain(ServerHttpSecurity http) {
http.cors().and().csrf()
.csrfTokenRepository(CookieServerCsrfTokenRepository.withHttpOnlyFalse())
.and()
.authorizeExchange()
.anyExchange().authenticated()
.and()
.oauth2ResourceServer()
.jwt();
return http.build();
}
@Bean
CorsConfigurationSource corsConfiguration() {
CorsConfiguration corsConfig = new CorsConfiguration();
corsConfig.applyPermitDefaultValues();
corsConfig.addAllowedMethod(HttpMethod.PUT);
corsConfig.addAllowedMethod(HttpMethod.DELETE);
corsConfig.setAllowedOrigins(Arrays.asList("*", "**"));
corsConfig.setAllowCredentials(true);
UrlBasedCorsConfigurationSource source =
new UrlBasedCorsConfigurationSource();
source.registerCorsConfiguration("/**", corsConfig);
return source;
}
public static void main(String[] args) {
SpringApplication.run(ApiGatewayServiceApplication.class, args);
}
}
I also using this Bean to change security config:
@Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
http
.authorizeRequests()
.requestMatchers(new AntPathRequestMatcher("/auth-service/**")).permitAll()
.anyRequest().permitAll()
.and()
.httpBasic()
.and()
.csrf().disable()
.cors().disable();
return http.build();
}
everythins is OK.When calling api with token in header in Postman everything works fine. But we try to develop a sample JS and ReactJs code to obtain API we receive error 401 and console log and trace it shows me that request headers authorization not accepted. We research and find this post: Spring Gateway and Auth0: IllegalArgumentException: Unable to find GatewayFilterFactory with name TokenRelay that say adding TokenRelay on config. I Added TokenRelay on config and try again but error exists.
everythins is OK.When calling api with token in header in Postman everything works fine. But we try to develop a sample JS and ReactJs code to obtain API we receive error 401 and console log and trace it shows me that request headers authorization not accepted. We research and find this post: Spring Gateway and Auth0: IllegalArgumentException: Unable to find GatewayFilterFactory with name TokenRelay that say adding TokenRelay on config. I Added TokenRelay on config and try again but error exists.
Upvotes: 0
Views: 652
Reputation: 113
TokenRelay filter has a bug that is fixed in version 4.1.1 of spring-cloud-gateway https://github.com/spring-cloud/spring-cloud-gateway/releases/tag/v4.1.1
Upvotes: -1
Reputation: 12754
The TokenRelay
filter replaces a session cookie with the access token in session. To have this access token in session, it of course needs sessions, but also oauth2Login()
(Spring OAuth2 client implementation with an authorization_code
registration).
Do not mix oauth2Login()
and oauth2ResourceServer()
in a single SecurityFilterChain
, security needs are too different:
authorization_code
401 Unauthorized
from a resource server (missing or invalid token)302 Redirect to login
(the session does not contain an authorized client yet)According to latest recommendations, we should avoid using "public" OAuth2 clients, and anything running in a browser or a mobile application can only be a "public" OAuth2 client => the code in your browser (plain Javascript or SPA framework) should not fetch tokens from the authorization server.
You should probably:
oauth2Login()
and the TokenRelay
filter (minimal dependencies are spring-boot-starter-oauth2-client
and spring-cloud-gateway
)oauth2ResourceServer()
(minimal dependencies are spring-boot-starter-oauth2-resource-server
and spring-boot-starter-web
or spring-boot-starter-webflux
)The requests from the browser to the Gateway will be authorized with session cookie (and should be protected against CSRF attacks).
The requests from the Gateway (or Postman) to the REST APIs will be authorized with access token.
In the case of a framework with a server-side rendering part, like next.js offers for instance, it is OK to use a lib implementing the OAuth2 client in that server-side part: this server side lib can implement the same important features regarding OAuth2 and requests authorization:
TokenRelay
does on spring-cloud-gateway
)Upvotes: 0