Spring Boot 3.0.2 adds "continue" query parameter to request URL after login - spring

I just upgraded my project from Spring boot 2.7.7 to 3.0.2 and I'm seeing some weird behavior.
When I login to my application Spring adds "continue" query parameter to URL. It wasn't like this in 2.7.7. Is there something which I'm missing?
I use formLogin and have implemenatation of AuthenticationSuccessHandler, though it doesn't add any parameters.

This is covered in Spring Security's migration guide.
Basically, the query parameter was added to make so that Spring Security knows whether to query the session on that request or not. It is a performance optimization.
You can change the value by configuring the RequestCache like so:
#Bean
SecurityFilterChain springSecurity(HttpSecurity http) throws Exception {
HttpSessionRequestCache requestCache = new HttpSessionRequestCache();
requestCache.setMatchingRequestParameterName("mycustomparameter");
http
// ...
.requestCache((cache) -> cache
.requestCache(requestCache)
);
return http.build();
}
Or, if you don't want this performance optimization, you can turn it off in the same way:
#Bean
SecurityFilterChain springSecurity(HttpSecurity http) throws Exception {
HttpSessionRequestCache requestCache = new HttpSessionRequestCache();
requestCache.setMatchingRequestParameterName(null);
http
// ...
.requestCache((cache) -> cache
.requestCache(requestCache)
);
return http.build();
}

Related

Unable to make the oauth2Login as stateless

I have provided the cookie based authorization request repository to oauth2Login() dsl to make it as stateless. but when I add the session creation policy as STATELESS , the oauth2 login is not working and returning "too many callbacks" error in UI page.
I have used the following oauth2Login config. for login with google oauth2 provider.
#Autowired
private HttpCookieOAuth2AuthorizationRequestRepository httpCookieOAuth2AuthorizationRequestRepository;
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.csrf().csrfTokenRepository(new CookieCsrfTokenRepository())
.ignoringAntMatchers("/oauth2/authorization/google")
.and()
.sessionManagement(sessionMgmtConfig -> sessionMgmtConfig.sessionCreationPolicy(SessionCreationPolicy.STATELESS))
.authorizeHttpRequests(authorize -> authorize
.anyRequest().authenticated())
.oauth2Login(oauth2Config -> oauth2Config
.authorizationEndpoint(config -> config.authorizationRequestRepository(httpCookieOAuth2AuthorizationRequestRepository))
.userInfoEndpoint(config -> config.oidcUserService(oidcUserOAuth2UserService()))
.successHandler(authenticationSuccessHandler())
)
;//.logout(logoutConfig -> logoutConfig.addLogoutHandler(logoutHandler()))
}
public AuthenticationSuccessHandler authenticationSuccessHandler() {
SavedRequestAwareAuthenticationSuccessHandler successHandler = new SavedRequestAwareAuthenticationSuccessHandler();
successHandler.setRequestCache(new CookieRequestCache());
return successHandler;
}
If I comment out the session management line, it is working as expected and creating the JSESSION but when not working if I uncomment this part. Am I missing something?
It is not so easy to have oauth2Login and stateless sessionManagement.
The problem is that Spring needs to store information about OAuth 2.0 state parameter. Normally it is stored in session but when you disable it, Spring gets crazy ("too many callbacks") because it can not find it.
To solve that problem you can use your own cookie to store state parameter.
This can be done by providing a custom implementation of AuthorizationRequestRepository<OAuth2AuthorizationRequest>.
There is a nice blog post descriging everything in more details.
https://www.jessym.com/articles/stateless-oauth2-social-logins-with-spring-boot
What is the purpose of the 'state' parameter in OAuth authorization request

How to get ServerWebExchange object in SecurityWebFilterChain bean

Hello Spring WebFlux community,
I have implemented x509 based authentication in spring webflux security bean using below code:
#Bean
public SecurityWebFilterChain securityWebFilterChain(ServerHttpSecurity http) {
ReactiveAuthenticationManager authMan = auth0 -> {
..//lambda logic of setAuthenticated true, HERE I NEED TO ACCESS CURRENT REQUEST HEADERS VALUE BEFORE SETTING IT TO TRUE
};
http.x509(x509 -> x509
.principalExtractor(myx509PrincipalExtractor())
.authenticationManager(authMan ))
.authorizeExchange(exchanges ->
exchanges.anyExchange().authenticated());
return http.build();
}
Now, please observe the comment part inside auth0 lambda, there I need to access header values of current request. How can I get that ?

Spring Security Configuration: Basic Auth + Spring Cloud Gateway

I've got a Reactive Spring Boot application, which is responsible for routing requests to downstream services, using Spring Cloud Gateway (i.e. it's an API gateway). The app has some actuator endpoints, that need to be secured, hence I want to use just a simple security for this like basic auth.
I'd like to configure the app, to require requests to /actuator/refresh to be authorized using basic auth (with a configured Spring security user and password). All requests to other endpoints, even if they include basic auth, only need to be passed to the downstream service.
My current Spring security configuration:
#Bean
#Order(1)
SecurityWebFilterChain securityWebFilterChain(final ServerHttpSecurity http) {
http.authorizeExchange(exchanges -> {
exchanges.matchers(EndpointRequest.toAnyEndpoint().excluding(HealthEndpoint.class, InfoEndpoint.class)).hasRole("ACTUATOR"); // requires Http Basic Auth
});
http.httpBasic(withDefaults()); // if not enabled, you cannot get the ACTUATOR role
return http.build();
}
#Bean
#Order(2)
SecurityWebFilterChain permitAllWebFilterChain(final ServerHttpSecurity http) {
http.authorizeExchange(exchanges -> exchanges.anyExchange().permitAll()); // allow unauthenticated access to any endpoint (other than secured actuator endpoints?)
http.httpBasic(ServerHttpSecurity.HttpBasicSpec::disable); // disable Http Basic Auth for all other endpoints
return http.build();
}
The request meant for the downstream service is not propagated by the API gateway. The spring boot service returns a 401 in this setup, while a 200 is expected / required.
Any ideas why this configuration is not working / how it should be configured otherwise?
Im not sure what is broken, but have you tried combining them and just have one filter?
#EnableWebFluxSecurity
public class MyExplicitSecurityConfiguration {
#Bean
public MapReactiveUserDetailsService userDetailsService() {
UserDetails user = User.withDefaultPasswordEncoder()
.username("user")
.password("user")
.roles("ACTUATOR")
.build();
return new MapReactiveUserDetailsService(user);
}
#Bean
SecurityWebFilterChain securityWebFilterChain(final ServerHttpSecurity http) {
http.authorizeExchange(exchanges -> {
exchanges.matchers(EndpointRequest.toAnyEndpoint()
.excluding(HealthEndpoint.class, InfoEndpoint.class))
.hasRole("ACTUATOR");
exchanges.anyExchange().permitAll();
}).httpBasic(withDefaults());
return http.build();
}
}
another good thing could be to enable debug logging and see what fails.
this is done by defining in application.properties
logging.level.org.springframework.security=DEBUG

Spring Boot 2.0 web flux custom authentication -- how to?

There are plenty of examples of minimal configurations of Spring Boot 2.0 security which compile or don't depending on which milestone or release candidate you try.
What is a minimal configuration that is not HTTP Basic, that will (1) let me access the HTTP request (headers, cookies, etc.) and also call my own authentication manager?
I would like to look at the headers and cookies, and decide from those who the user is, and whether or not the user is authenticated. How I do that should not matter to this answer -- the question is, what is the minimal Spring security config in order to allow me to hook in to the security infrastructure, so that my authentication is there in the reactive endpoints?
EDIT:
This works with Spring Boot 2.0.0.RC2, so my question could be, is this a correct way to introduce custom authentication into Spring Security?
#Configuration
#EnableWebFluxSecurity
public class SecurityConfiguration {
#Bean
public SecurityWebFilterChain springSecurityFilterChain(ServerHttpSecurity http) {
AuthenticationWebFilter authenticationFilter = new AuthenticationWebFilter(authentication -> {
authentication.setAuthenticated(true);
return Mono.just(authentication);
});
authenticationFilter.setAuthenticationConverter(serverWebExchange ->
Mono.just(new AbstractAuthenticationToken(new ArrayList<>()) {
#Override
public Object getCredentials() {
return null;
}
#Override
public Object getPrincipal() {
return "jim";
}
}));
return http.addFilterAt(authenticationFilter, SecurityWebFiltersOrder.FORM_LOGIN)
.authorizeExchange()
.anyExchange()
.authenticated()
.and()
.build();
}
}
You can imagine that in the converter, I am free to look into the request by way of serverWebExchange and inspect any headers or cookies I wish, and that later in the upper lambda (standing in for ReactiveAuthenticationManager) I can actually decide whether or not it should be authenticated.

How to use Spring WebSessionIdResolver with Spring Security 5 in a Spring webflux application?

Currently I was experiencing the new Spring reactive stack, and want to use reactive features in Spring Session 2.0.
In traditional Servlet approach, Spring Session provides a HttpSessionStrategy to detect session in cookie or request headers. It is easy to use HeaderHttpSessionStrategy to implement a token like authentication(by default the he name is X-AUTH-TOKEN) for RESTful APIs.
Spring 5 core provides a WebSessionIdResolver to do the same thing for Reactive environment.
But when use it with Spring Security and wish it worked as traditional way, I can not get it work.
The SessionConfig file.
#EnableSpringWebSession
public class SessionConfig {
#Bean
public ReactorSessionRepository sessionRepository() {
return new MapReactorSessionRepository(new ConcurrentHashMap<>());
}
#Bean
public WebSessionIdResolver headerWebSessionIdResolver() {
HeaderWebSessionIdResolver resolver = new HeaderWebSessionIdResolver();
resolver.setHeaderName("X-SESSION-ID");
return resolver;
}
}
The partial SecurityConfig.
#EnableWebFluxSecurity
class SecurityConfig {
#Bean
SecurityWebFilterChain springWebFilterChain(HttpSecurity http) throws Exception {
return http
.authorizeExchange()
.pathMatchers(HttpMethod.GET, "/posts/**").permitAll()
.pathMatchers(HttpMethod.DELETE, "/posts/**").hasRole("ADMIN")
//.pathMatchers("/users/{user}/**").access(this::currentUserMatchesPath)
.anyExchange().authenticated()
.and()
.build();
}
A test rest controller file, it returns the current Session ID.
#RestController
public class SessionRestController {
#GetMapping("/sessionId")
public Map<String, String> sessionId(WebSession session){
Map<String, String> map = new HashMap<>();
map.put("id", session.getId());
return map ;
}
}
When I started up the application, and use curl to access the /sessionId, there is no session info the response header.
curl -v -u "user:password" http://localhost:8080/sessionId
And I got the session id in the query result and put it into request headers to access the protected resources and got 401.
curl -v -X POST -H "X-SESSION-ID:xxxx" http://localhost:8080/posts
Update: A working sample can be found here.
Spring Framework's spring-web module defaults to using it's CookieWebSessionIdResolver, which is based on cookies. If you create an alternative bean of type HeaderWebSessionIdResolver, it will get picked up automatically by Spring Session and switch to a header-based strategy.
In either strategy, it's geared to read the incoming ServerExchange headers, and look up the session id, whether that is reading the Cookie header or the SESSION http header.
These strategies also create response headers, whether that is a set-cookie directive for the client (web browser or your code) to populate the Cookie, or to give you the SESSION header (default name for the HeaderWebSessionIdResolver's header name).

Resources