How to modify query param in webflux filter - filter

So any ideal how to modify query param in webflux filter, this code didn't work;
public class MyFilter implements WebFilter {
#Override
public Mono<Void> filter(ServerWebExchange exchange, WebFilterChain chain) {
ServerHttpRequest request = exchange.getRequest();
// didn't work, throw UnSupportOperationException
//request.getQueryParams().add("key1", "value1");
return chain.filter(exchange);
}
}

I am not sure about how you can modify the query param but the reason why you are not able to modify the params through the method getQueryParams() is because it returns a read only map. Check this link
https://docs.spring.io/spring-framework/docs/current/javadoc-api/org/springframework/http/server/reactive/ServerHttpRequest.html#getQueryParams--

As #Prakher Jindal explained request.getQueryParams() returns a read-only map,
but you can add query params by writing a filter like this:
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import org.springframework.cloud.gateway.filter.GatewayFilter;
import org.springframework.cloud.gateway.filter.OrderedGatewayFilter;
import org.springframework.cloud.gateway.filter.RouteToRequestUrlFilter;
import org.springframework.cloud.gateway.filter.factory.AbstractGatewayFilterFactory;
import org.springframework.stereotype.Component;
import org.springframework.web.util.UriComponents;
import org.springframework.web.util.UriComponentsBuilder;
import java.net.URI;
import java.util.Optional;
import static org.springframework.cloud.gateway.support.ServerWebExchangeUtils.GATEWAY_REQUEST_URL_ATTR;
#Component
public class MyRewriteGatewayFilter extends AbstractGatewayFilterFactory<MyRewriteGatewayFilter.Config> {
public SecurityRewriteGatewayFilter() {
super(Config.class);
}
#Override
public GatewayFilter apply(Config config) {
return new OrderedGatewayFilter((exchange, chain) -> {
Optional.of(exchange)
.map(e -> (URI) e.getAttribute(GATEWAY_REQUEST_URL_ATTR))
.map(URI::toString)
.map(uri -> uri + "?key=" + "value")
.map(UriComponentsBuilder::fromUriString)
.map(UriComponentsBuilder::build)
.map(UriComponents::toUri)
.ifPresent(u -> exchange.getAttributes().put(GATEWAY_REQUEST_URL_ATTR, u));
return chain.filter(exchange.mutate().build());
}
, RouteToRequestUrlFilter.ROUTE_TO_URL_FILTER_ORDER + 1);
}
#Data
public static class Config {
}
}
Since this filter is not a global filter, make sure to add this to your application.yml file for the
routes on which you want to apply it. Something like this:
spring:
cloud:
gateway:
routes:
- id: my-routes
uri: http://localhost:8082
predicates:
- Path=/api/**
filters:
- name: MyRewriteGatewayFilter

Related

How to validate a model model in web flux SpringBoot

I'm trying to move from traditional approach to Reactive style. Early days for me. One of the challenge I came into and could not make much progress is on model validation. With RestControllers, it was as easy as #Valid.
I don't see anything out there to make it happen for Webflux way of doing things
package com.reactive.sbhello.handler;
import com.reactive.sbhello.model.Order;
import com.reactive.sbhello.service.OrderService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.MediaType;
import org.springframework.stereotype.Component;
import org.springframework.web.reactive.function.BodyInserters;
import org.springframework.web.reactive.function.server.ServerRequest;
import org.springframework.web.reactive.function.server.ServerResponse;
import reactor.core.publisher.Mono;
import javax.validation.Validator;
#Component
public class OrderHandler {
#Autowired
private OrderService orderService;
private final Validator validator;
public OrderHandler(Validator validator) {
this.validator = validator;
}
public Mono<ServerResponse> getAll(ServerRequest request) {
return ServerResponse.ok()
.contentType(MediaType.APPLICATION_JSON)
.body(orderService.getAllOrders(),
Order.class
);
}
public Mono<ServerResponse> getOrderInfo(ServerRequest request) {
var orderId = request.pathVariable("orderId");
var response = orderService.getOrderById(Integer.parseInt(orderId));
return response.collectList()
.flatMap(orders -> {
if(orders.isEmpty()) {
return ServerResponse.badRequest().body(BodyInserters.fromValue("Invalid OrderId"));
} else {
return ServerResponse.ok().body(BodyInserters.fromValue(orders));
}
});
}
public Mono<ServerResponse> addOrder(ServerRequest request) {
return request.bodyToMono(Order.class)
.flatMap(order -> orderService.addOrder(order))
.flatMap(order -> ServerResponse.ok()
.contentType(MediaType.APPLICATION_JSON)
.body(BodyInserters.fromValue(order)));
}
}
"addOrder" function at the moment lacks any validation. As a result, null values go through. Is there anyway to validate apart from doing it in service and bubble up the error? Or should I stick to RestController approach and still use streaming from there.

AuthenticationManager.authenticate method not getting called

I am trying to follow the API Key authentication code from this answer:
https://stackoverflow.com/a/48448901
I created my filter class:
package com.propfinancing.CADData.web;
import javax.servlet.http.HttpServletRequest;
import org.slf4j.LoggerFactory;
import org.springframework.security.web.authentication.preauth.AbstractPreAuthenticatedProcessingFilter;
public class PullAPIKeyFromHeaderFilter extends AbstractPreAuthenticatedProcessingFilter {
private String apiKeyHeaderName;
public PullAPIKeyFromHeaderFilter(String apiKeyHeaderName) {
this.apiKeyHeaderName = apiKeyHeaderName;
}
#Override
protected Object getPreAuthenticatedPrincipal(HttpServletRequest request) {
String headerValue = request.getHeader(apiKeyHeaderName);
return request.getHeader(headerValue);
}
#Override
protected Object getPreAuthenticatedCredentials(HttpServletRequest request) {
return apiKeyHeaderName;
}
}
And then I implemented my security configuration:
package com.propfinancing.CADData.web;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.annotation.Order;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.BadCredentialsException;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.config.annotation.web.configurers.ExpressionUrlAuthorizationConfigurer;
import org.springframework.security.config.http.SessionCreationPolicy;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;
#Configuration
#EnableWebSecurity
#Order(1)
public class APIKeySecurityConfig extends WebSecurityConfigurerAdapter {
#Value("${caddata.apiKey.header.name}")
private String apiKeyHeaderName;
#Value("${caddata.apiKey}")
private String apiKey;
#Override
protected void configure(HttpSecurity httpSecurity) throws Exception {
PullAPIKeyFromHeaderFilter pullAPIKeyFromHeaderfilter = new PullAPIKeyFromHeaderFilter(apiKeyHeaderName);
AuthenticationManager authManager = new AuthenticationManager() {
#Override
public Authentication authenticate(Authentication authentication)
throws AuthenticationException {
String principal = (String) authentication.getPrincipal();
if (!apiKey.equals(principal))
throw new BadCredentialsException("Invalid API key");
authentication.setAuthenticated(true);
return authentication;
}
};
pullAPIKeyFromHeaderfilter.setAuthenticationManager(authManager);
httpSecurity.antMatcher("/**");
httpSecurity.addFilter(pullAPIKeyFromHeaderfilter);
httpSecurity.requiresChannel().anyRequest().requiresSecure();
httpSecurity.csrf().disable();
httpSecurity.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS);
ExpressionUrlAuthorizationConfigurer<HttpSecurity>.ExpressionInterceptUrlRegistry urlAuthConfigurer = httpSecurity.authorizeRequests();
ExpressionUrlAuthorizationConfigurer<HttpSecurity>.AuthorizedUrl authorizedUrl = urlAuthConfigurer.anyRequest();
authorizedUrl.authenticated();
}
}
When I do an external call to the application with the header as part of the request, I get a 403 Forbidden response.
I can see the filter pulling the key from the header. That part is working.
But, the authenticate() method is not being called to check if the header is valid.
I am not sure what I missed, the code looks the same to me.
Any ideas?
Looks like the wrong base class, per https://docs.spring.io/spring-security/site/docs/4.0.x/apidocs/org/springframework/security/web/authentication/preauth/AbstractPreAuthenticatedProcessingFilter.html :
The purpose is then only to extract the necessary information on the
principal from the incoming request, rather than to authenticate them.
Try extending https://docs.spring.io/spring-security/site/docs/4.0.x/apidocs/org/springframework/security/web/authentication/AbstractAuthenticationProcessingFilter.html instead.
I was not able to get the code above to work, but I changed it to use the second answer on the thread (Using a Filter) https://stackoverflow.com/a/63852212 It works as expected.

Spring Boot: "relaying" basic auth from REST controller to RestTemplate

I'm working with two Spring Boot applications, let's call them ServiceA and ServiceB, both exposing a REST API.
ServiceA is called by end users from the browser via a frontend app (we use #RestController classes). On some calls, ServiceA has to call ServiceB (using RestTemplate). We've got authentication and authorization sorted out for our target environment, but for testing locally we are relying on Basic Auth instead, and that's where we're hitting a snag: we would like ServiceA to re-use the Basic Auth credentials the user provided when calling Service B.
Is there an easy way to pass the Basic Auth credentials used on the call to our REST controller to the RestTemplate call?
Quick and dirty solution
The easiest way to do this would be:
import org.springframework.http.HttpEntity;
import org.springframework.http.HttpHeaders;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestHeader;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.client.RestTemplate;
#RestController
class SomeController {
private final RestTemplate restTemplate = new RestTemplate();
#PostMapping("/delegate/call")
public void callOtherService(#RequestHeader(HttpHeaders.AUTHORIZATION) String authorization) {
HttpHeaders headers = new HttpHeaders();
headers.add(HttpHeaders.AUTHORIZATION, authorization);
restTemplate.postForEntity("other-service.com/actual/call", new HttpEntity<Void>(null, headers), Void.class);
// handling the response etc...
}
}
Using interceptors and RestTemplateCustomizer
I didn't want to change to add an extra parameter on each controller method, and I wanted a way to enable or disable this behavior depending on the environment, so here is a slightly more complicated solution that can be enabled using Spring profiles, and doesn't touch the controllers:
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpRequest;
import org.springframework.http.client.ClientHttpRequestExecution;
import org.springframework.http.client.ClientHttpRequestInterceptor;
import org.springframework.http.client.ClientHttpResponse;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
public class BasicAuthPropagationInterceptor implements HandlerInterceptor, ClientHttpRequestInterceptor {
private final ThreadLocal<String> cachedHeader = new ThreadLocal<>();
#Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
final String header = request.getHeader(HttpHeaders.AUTHORIZATION);
cachedHeader.set(header);
return true;
}
#Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
cachedHeader.remove();
}
#Override
public ClientHttpResponse intercept(HttpRequest request, byte[] body, ClientHttpRequestExecution execution) throws IOException {
String ch = cachedHeader.get();
if (!request.getHeaders().containsKey(HttpHeaders.AUTHORIZATION) && ch != null) {
request.getHeaders().add(HttpHeaders.AUTHORIZATION, ch);
}
return execution.execute(request, body);
}
}
This stores the received header in a ThreadLocal and adds it with an interceptor for RestTemplate.
This can then be configured as such:
import org.springframework.boot.web.client.RestTemplateCustomizer;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Profile;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
#Configuration
#Profile("LOCAL")
class LocalConfiguration implements WebMvcConfigurer {
private final BasicAuthPropagationInterceptor basicAuthPropagationInterceptor
= new BasicAuthPropagationInterceptor();
#Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(basicAuthPropagationInterceptor);
}
#Bean
RestTemplateCustomizer restTemplateCustomizer() {
return restTemplate -> restTemplate.getInterceptors().add(basicAuthPropagationInterceptor);
}
}
RestTemplate obtained by using the default RestTemplateBuilder bean will then automatically set the Authorization HTTP header if it's available in the current thread.

How to get BearerTokenAuthentication from SecurityContext in a GlobalFilter in Spring Cloud Gateway

Spring Cloud Gateway as a OAuth2ResourceServer with following Authorisation Config:
#Bean
SecurityWebFilterChain springSecurityFilterChain(ServerHttpSecurity http) {
http
.authorizeExchange(exchanges ->
exchanges
.anyExchange().authenticated()
)
.oauth2ResourceServer(OAuth2ResourceServerSpec::jwt)
return http.build();
}
I have a global filter in place which is responsible for performing some functions at each valid authenticated request, something like this:
#Service
public class CustomGlobal implements GlobalFilter {
#Autowired
BearerTokenAuthentication authentication;
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
// access request headers, and perform some logic
// extract details from the JWT token, and perform some logic
log.info(authentication.getTokenAttributes.get("sub"));
// ^ in the above line there's a NullPointerException, since instance
// BearerTokenAuthentication is not set, or not visible at a GlobalFilter class
return chain.filter(exchange);
}
}
I am still in a learning phase. Any possible leads would be appreciated.
I did that this way(Note you should change WebFilter to GlobalFilter).
Add into your pom
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-oauth2-jose</artifactId>
<version>5.4.6</version>
</dependency>
Then Filter should be like
package filter;
import lombok.extern.log4j.Log4j2;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.security.oauth2.core.DelegatingOAuth2TokenValidator;
import org.springframework.security.oauth2.jwt.*;
import org.springframework.util.StringUtils;
import org.springframework.web.server.ServerWebExchange;
import org.springframework.web.server.WebFilter;
import org.springframework.web.server.WebFilterChain;
import reactor.core.publisher.Mono;
#Log4j2
public class CustomGlobal implements WebFilter {
public static final String HEADER_PREFIX = "Bearer ";
private final ReactiveJwtDecoder jwtDecoder;
public ReactiveJwtDecoder createDecoder(String issuer, String jwkUrl) {
var jwtDecoder = NimbusReactiveJwtDecoder.withJwkSetUri(jwkUrl).build();
jwtDecoder.setJwtValidator(new DelegatingOAuth2TokenValidator<>(
new JwtIssuerValidator(issuer),
new JwtTimestampValidator()));
return jwtDecoder;
}
protected CustomGlobal(String issuer, String jwkUrl) {
this.jwtDecoder = createDecoder(issuer, jwkUrl);
}
#Override
public Mono<Void> filter(ServerWebExchange exchange, WebFilterChain chain) {
return Mono
.defer(() -> {
var token = resolveToken(exchange.getRequest());
if (!StringUtils.hasText(token)) {
throw new BadJwtException("Authorisation token is invalid");
}
return jwtDecoder.decode(token);
})
.flatMap(tokenJwt -> {
log.info(tokenJwt.getClaimAsString("sub"));
return chain.filter(exchange);
})
.onErrorResume(err -> handleError(exchange));
}
private Mono<Void> handleError(ServerWebExchange exchange) {
exchange.getResponse().setRawStatusCode(HttpStatus.UNAUTHORIZED.value());
exchange.getResponse().getHeaders().add("Content-Type", "application/json");
return exchange.getResponse().setComplete();
}
private String resolveToken(ServerHttpRequest request) {
String bearerToken = request.getHeaders().getFirst(HttpHeaders.AUTHORIZATION);
if (StringUtils.hasText(bearerToken) && bearerToken.startsWith(HEADER_PREFIX)) {
return bearerToken.substring(7).trim();
}
return "";
}
}
Next step would be to create configuration
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.client.reactive.ReactorClientHttpConnector;
#Configuration
public class CustomGlobalConfig {
#Value("${jwt.iss}")
private String issuer;
#Value("${jwt.jwk-uri}")
private String jwkUrl;
#Bean
CustomGlobal createFilterBean() {
return new CustomGlobal(this.issuer, this.jwkUrl);
}
}

Need to get authenticated user name from the OAuth2User in Spring Cloud Gateway routes

I am a newbie to Spring Cloud Gateway. I have created a simple SCG application and my use case is to forward the authenticated username to my downstream application via http request header.
This is my SpringBoot class
package com.xyz;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.autoconfigure.security.oauth2.resource.OAuth2ResourceServerProperties.Jwt;
import org.springframework.cloud.gateway.route.RouteLocator;
import org.springframework.cloud.gateway.route.builder.RouteLocatorBuilder;
import org.springframework.cloud.security.oauth2.gateway.TokenRelayGatewayFilterFactory;
import org.springframework.context.annotation.Bean;
import org.springframework.security.core.annotation.AuthenticationPrincipal;
import org.springframework.security.oauth2.client.OAuth2AuthorizedClient;
import org.springframework.security.oauth2.client.annotation.RegisteredOAuth2AuthorizedClient;
import org.springframework.security.oauth2.core.user.OAuth2User;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping;
#Controller
#SpringBootApplication
public class OracleSSOMultiplexer {
#Autowired
private TokenRelayGatewayFilterFactory filterFactory;
#GetMapping("/")
public String index(Model model,
#RegisteredOAuth2AuthorizedClient OAuth2AuthorizedClient authorizedClient,
#AuthenticationPrincipal OAuth2User oauth2User) {
model.addAttribute("userName", oauth2User.getName());
model.addAttribute("clientName", authorizedClient.getClientRegistration().getClientName());
model.addAttribute("userAttributes", oauth2User.getAttributes());
return "index";
}
#Bean
public RouteLocator customRouteLocator(RouteLocatorBuilder builder) {
return builder.routes()
//.route("resource", r -> r.path("/resource")
.route(r -> r.path("/hyperion/**")
.filters(f -> f.filters(filterFactory.apply())
.addRequestHeader("HYPLOGIN", "scott") // I need to pass the authenticated username here instead of hardcoding it
.addResponseHeader("hyp-response", "hyp-response-header-val"))
.uri("http://localhost:8081/")
.id("hyperionModule"))
.build();
}
public static void main(String[] args) {
SpringApplication.run(OracleSSOMultiplexer.class, args);
}
}
and this my application.yml
server:
port: 8080
logging:
level:
root: INFO
org.springframework.web: INFO
org.springframework.web.HttpLogging: DEBUG
org.springframework.security: DEBUG
org.springframework.security.oauth2: DEBUG
org.springframework.cloud.gateway: DEBUG
spring:
autoconfigure:
# TODO: remove when fixed https://github.com/spring-projects/spring-security/issues/6314
exclude: org.springframework.boot.actuate.autoconfigure.security.reactive.ReactiveManagementWebSecurityAutoConfiguration
thymeleaf:
cache: false
security:
oauth2:
client:
registration:
gateway:
provider: pingfederate
client-id: oidchyperion
client-secret: Lms#12345
authorization-grant-type: authorization_code
redirect-uri: http://localhost:8080/login/oauth2/code/gateway
scope: openid
provider:
pingfederate:
authorization-uri: https://xyz:9035/as/authorization.oauth2
token-uri: https://xyz:9035/as/token.oauth2
user-info-uri: https://xyz:9035/idp/userinfo.openid
user-name-attribute: sub
jwk-set-uri: https://xyz:9035/pf/JWKS
# cloud:
# gateway:
# routes:
# - id: resource
# uri: http://resource:9000
# predicates:
# - Path=/resource
# filters:
# - TokenRelay=
# - RemoveRequestHeader=Cookie
I am able to perform OAuth Authentication and able to display the authenticated user information using the Model at the index.html. I need now to just pass the logged in user's username (oauth2user.getName()) in the request header but I am not able to figure out.
Any help in this regards is appreciated.
Thanks,
Thani
package com.likeminds.filter;
import java.security.Principal;
import java.util.Collections;
import java.util.List;
import org.springframework.cloud.gateway.filter.GatewayFilter;
import
org.springframework.cloud.gateway.filter.factory.AbstractGatewayFilterFactory;
import org.springframework.stereotype.Component;
#Component
public class AuthNUserHeaderFilterFactory extends
AbstractGatewayFilterFactory<AuthNUserHeaderFilterFactory.Config> {
private static final String VALUE = "value";
public AuthNUserHeaderFilterFactory() {
super(Config.class);
}
#Override
public Config newConfig() {
return new Config();
}
#Override
public List<String> shortcutFieldOrder() {
return Collections.singletonList(VALUE);
}
#Override
public GatewayFilter apply(Config config) {
//Custom Pre Filter. Suppose we can extract JWT and perform
Authentication
return (exchange, chain) -> exchange.getPrincipal()
.map(Principal::getName)
.defaultIfEmpty("Default User")
.map(userName -> {
//adds header to proxied request
System.out.println("Config value ="+config.value);
exchange.getRequest().mutate().header(config.value,
userName).build();
System.out.println("Config First pre header filter" +
exchange.getRequest().getHeaders());
return exchange;
})
.flatMap(chain::filter);
}
public static class Config {
private String value;
public String getValue() {
return value;
}
public void setValue(String value) {
this.value = value;
}
}
}
You can write a simple gateway filter config and intercept the request before passing the request to downstream microservices if you are using it as a gateway.
If you are using OAuth2 Authentication then you should be able to get the users information by adding below lines of code:
if (principal instanceof OAuth2AuthenticationToken) {
//Get username from Principal
String userName = principal.getName();
//Get user infor from spring security context
SecurityContextImpl context = exchange.getSession().block().getAttribute("SPRING_SECURITY_CONTEXT");
DefaultOidcUser principal1 = (DefaultOidcUser) context.getAuthentication().getPrincipal();
String fullName = principal1.getUserInfo().getFullName();
String emailId = principal1.getUserInfo().getEmail();
}
Once you get the use information, you can add this information in the request header or in cookies and pass on to the downstream microservices.
// Adding cookies
session = new HttpCookie("SESSION", exchange.getSession().block().getId()).toString();
// adds header to proxied request
exchange.getRequest().mutate().header("X-Auth-Username", new String[] { userName })
.header("X-Auth-Userid", new String[] { userId })
.header("X-Auth-Email", new String[] { emailId }).header("cookie", session)
.build();
Below is complete code which you can use:
import java.net.HttpCookie;
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.authentication.OAuth2AuthenticationToken;
import org.springframework.security.oauth2.core.oidc.user.DefaultOidcUser;
import reactor.core.publisher.Mono;
#Configuration
public class GatewayConfig {
#SuppressWarnings("deprecation")
#Bean
public GlobalFilter customGlobalFilter() {
return (exchange, chain) -> exchange.getPrincipal().map(principal -> {
String emailId = "";
String session = "";
String userName = "";
if (principal instanceof OAuth2AuthenticationToken) {
//Get username from Principal
userName = principal.getName();
SecurityContextImpl context = exchange.getSession().block().getAttribute("SPRING_SECURITY_CONTEXT");
DefaultOidcUser principal1 = (DefaultOidcUser) context.getAuthentication().getPrincipal();
emailId = principal1.getUserInfo().getEmail();
// Adding cookies
session = new HttpCookie("SESSION", exchange.getSession().block().getId()).toString();
}
// adds header to proxied request
exchange.getRequest().mutate().header("X-Auth-Username", new String[] { userName })
.header("X-Auth-Email", new String[] { emailId }).header("cookie", session)
.build();
return exchange;
}).flatMap(chain::filter).then(Mono.fromRunnable(() -> {
}));
}
}
You can use these headers and cookies in all the downstream microservices.

Resources