I have integrated Spring Cloud Gateway with OAuth2 server. It works well with single instance gateway. here is my security config.
@EnableWebFluxSecurity
public class GatewaySecurityConfiguration {
@Bean
public SecurityWebFilterChain securityWebFilterChain(ServerHttpSecurity http) {
http
.authorizeExchange().pathMatchers("/user/v3/api-docs", "/actuator/**").permitAll()
.anyExchange().authenticated()
.and()
.oauth2Login()
.and()
.csrf().disable();
return http.build();
}
But, When I scale gateway to 2 instances, some requests works expected, however some requests return 401.
load balancer (kubernetes nodeport service)
/
gateway gateway
/
(microservice clusters)
When I logged in first instance of gateway, the principal object is created successfully and also assign session to redis. If next request comes to second instance, it returns 401 because it has not principal.
how can i solve this problem?
ps: i am using redis for web sessions to commonize session informations between gateways.
2
Answers
TL;DR
You can share session principal information on Redis through WebSession. But you can't share access token(JWT), because they are stored in-memory at servers.
Long Answer
From Spring Cloud documentation (https://cloud.spring.io/spring-cloud-static/Greenwich.SR5/multi/multi__more_detail.html);
The first thing you know: When you login successfully, the access token(as jwt) is returned by oauth2 server, and server creates session and this session is mapped to access token on ConcurrentHashMap (authorizedClients instance InMemoryReactiveOAuth2AuthorizedClientService class).
When you request API Gateway to access microservices with your session id, the access token(jwt) is resolved by TokenRelayGatewayFilterFactory in gateway, and this access token is set in Authorization header, and the request is forwarding to microservices.
So, let me explain how TokenRelayGatewayFilterFactory works (assume that you use WebSession through Redis and you have 2 gateway instances and you logged in at instance-1.)
Solution-1
So, your requests should always go to the server where you are logged in to get valid access token.
Solution-2
InMemoryReactiveOAuth2AuthorizedClientService is only implementation of ReactiveOAuth2AuthorizedClientService. So, create new implementation that uses Redis, and then do it primary bean.
Notes:
The TokenRelayGatewayFilterFactory uses an in-memory data store to store the OAuth2AuthorizedClient which includes the (JWT) access token. This data store is not shared between multiple gateways.
To share the OAuth2AuthorizedClient information with Spring Session through Redis provide the following configuration:
For reactive WebSessions:
Further information for this configuration can be found at https://github.com/spring-projects/spring-security/issues/7889