I'm new on OAuth2 and Spring Cloud Gateway(And WebFlux things).
My team decided to move from Zuul gateway to Spring Cloud Gateway. And current Spring Cloud version is "Greenwich.SR1"
The problem is spring cloud gateway always response 401.
How to pass access token on Spring Cloud Gateway properly?
Auth server :
@EnableEurekaClient
@EnableAuthorizationServer
@SpringBootApplication
public class AuthServer {...} // jwtAccessTokenConverter bean included
Zuul server is :
@EnableEurekaClient
@EnableZuulProxy
@SpringBootApplication
public class ZuulServer {...}
Zuul server properties :
zuul:
sensitive-headers: Cookie,Set-Cookie
ignored-services: '*'
routes:
auth: /auth/**
Spring Cloud Gateway Server properties :
spring:
cloud:
gateway:
routes:
- id: auth
uri: lb://auth
predicates:
- Method=POST
- Path=/auth/**
filters:
- RemoveRequestHeader= Cookie,Set-Cookie
- StripPrefix=1
Spring Cloud server build.gradle :
plugins {
id 'java'
id "io.freefair.lombok" version "3.2.0"
id "org.springframework.boot" version "2.1.5.RELEASE"
id "io.spring.dependency-management" version "1.0.6.RELEASE"
}
version = '1.0.0-SNAPSHOT'
description = 'edge-service2'
sourceCompatibility = '11'
dependencies {
implementation platform("org.springframework.cloud:spring-cloud-dependencies:$springCloudVersion")
implementation "org.springframework.boot:spring-boot-starter-security"
implementation "org.springframework.cloud:spring-cloud-starter-netflix-eureka-client"
implementation "org.springframework.cloud:spring-cloud-starter-netflix-ribbon"
implementation "org.springframework.cloud:spring-cloud-starter-netflix-hystrix"
implementation('org.springframework.cloud:spring-cloud-starter-gateway')
implementation "org.springframework.cloud:spring-cloud-config-client"
implementation "de.codecentric:spring-boot-admin-starter-client:$springBootAdminVersion"
implementation "net.gpedro.integrations.slack:slack-webhook:1.4.0"
testImplementation "org.springframework.boot:spring-boot-starter-test"
}
springBoot {
buildInfo()
}
bootJar {
archiveName "${project.name}.jar"
}
You can create a custom configuration class and annotate it with @Configuration.
Step 1: Get the instance of ServerOAuth2AuthorizedClientRepository Interface
@Autowired
private ServerOAuth2AuthorizedClientRepository clientRegistrationRepository;
Step 2: Apply Global filter and fetch the token from the OAuth2AuthorizedClient
// For Getting token from request
SecurityContextImpl context =
exchange.getSession().toProcessor().block().getAttribute("SPRING_SECURITY_CONTEXT");
OAuth2AuthenticationToken oauthToken =
(OAuth2AuthenticationToken) context.getAuthentication();
Mono<OAuth2AuthorizedClient> authorizedClient = clientRegistrationRepository
.loadAuthorizedClient(oauthToken.getAuthorizedClientRegistrationId(),
context.getAuthentication(), exchange);
OAuth2AuthorizedClient client = authorizedClient.toProcessor().block();
String accessToken = client.getAccessToken().getTokenValue();
LOG.info("Access Token value: {}", accessToken);
Here is the complete configuration class which does the job perfectly.
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cloud.gateway.filter.GlobalFilter;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.core.context.SecurityContextImpl;
import org.springframework.security.oauth2.client.OAuth2AuthorizedClient;
import org.springframework.security.oauth2.client.authentication.OAuth2AuthenticationToken;
import org.springframework.security.oauth2.client.web.server.ServerOAuth2AuthorizedClientRepository;
import reactor.core.publisher.Mono;
@Configuration
public class GatewayConfig {
private static final Logger LOG = LoggerFactory.getLogger(GatewayConfig.class);
@Autowired
private ServerOAuth2AuthorizedClientRepository clientRegistrationRepository;
@SuppressWarnings("deprecation")
@Bean
public GlobalFilter customGlobalFilter() {
return (exchange, chain) -> exchange.getPrincipal().map(principal -> {
if (principal instanceof OAuth2AuthenticationToken) {
// For Getting token from request
SecurityContextImpl context =
exchange.getSession().toProcessor().block().getAttribute("SPRING_SECURITY_CONTEXT");
OAuth2AuthenticationToken oauthToken =
(OAuth2AuthenticationToken) context.getAuthentication();
Mono<OAuth2AuthorizedClient> authorizedClient = clientRegistrationRepository
.loadAuthorizedClient(oauthToken.getAuthorizedClientRegistrationId(),
context.getAuthentication(), exchange);
OAuth2AuthorizedClient client = authorizedClient.toProcessor().block();
String accessToken = client.getAccessToken().getTokenValue();
LOG.info("Access Token value: {}", accessToken);
}
return exchange;
}).flatMap(chain::filter).then(Mono.fromRunnable(() -> {
}));
}
}