I want to implement security on my Spring cloud gateway server by making it an oAuth2 resource server. The requests are getting authenticated against my spring security authorization server. For some requests I want to pass the userId
of the authenticated user as a request header to my downstream services.
Here's my route:
.route("create-deck", routeSpec -> routeSpec
.path("/decks")
.and()
.method(HttpMethod.POST)
.filters(
fs -> fs.addRequestHeader("userId", "ADD_USER_ID_HEADER_HERE"))
.uri("http://localhost:8082/"))
I have configured my authorization server to return userId
as principal name:
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
User user = ur.findByUsername(username);
if (user == null) {
log.error("User: {} not found", username);
throw new UsernameNotFoundException("User: " + username + " not found");
}
return new org.springframework.security.core.userdetails.User(String.valueOf(user.getId()), user.getPassword(), authorities);
}
Note: I have a custom domain object User that I store in my database.
The route is getting successfully authenticated and the token contains userId as subject. I just need help on how to get the subject and pass that to my downstream request as a request header.
I am using the following dependencies:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-oauth2-resource-server</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-oauth2-jose</artifactId>
<version>5.6.2</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-webflux</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-gateway</artifactId>
</dependency>
You could create custom filter that will be applied to all requests. Here is an example when user
is a part of the jwt token.
public class UserHeaderFilter implements GlobalFilter {
@Override
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
return ReactiveSecurityContextHolder.getContext()
.filter(c -> c.getAuthentication() != null)
.flatMap(c -> {
JwtAuthenticationToken jwt = (JwtAuthenticationToken) c.getAuthentication();
String user = (String) jwt.getTokenAttributes().get("user");
if (Strings.isNullOrEmpty(user)) {
return Mono.error(
new AccessDeniedException("Invalid token. User is not present in token.")
);
}
ServerHttpRequest request = exchange.getRequest().mutate()
.header("x-user", user).build();
return chain.filter(exchange.mutate().request(request).build());
})
.switchIfEmpty(chain.filter(exchange));
}
}