I'm upgrading a working application from Spring Boot 2.6.5 to 2.7.8 (Spring Security 5.7.6) so that I can better upgrade to 3.0.
Logout was working fine before. But after upgrading to Sprint Boot 2.7.8 the logout handling is hanging and never redirecting to the logoutSuccessUrl().
Here is the complete filter chain configuration:
http
.authorizeHttpRequests((authz) -> authz
.antMatchers("/webjars/**","/login/**","/mobile-manifest.json","/service-worker.js","/cache.manifest","/favicon.ico","/async/**","/api/**").permitAll()
.anyRequest().authenticated()
)
.httpBasic(withDefaults())
.formLogin(formLogin -> formLogin
.loginPage("/login")
.permitAll()
.loginProcessingUrl("/login")
.successHandler(savedRequestAwareAuthenticationSuccessHandler())
.failureUrl("/login?loginFailed=true"))
.logout( logout -> logout
.logoutUrl("/logout")
.logoutSuccessUrl("/login?logoutSuccess=true")
.invalidateHttpSession(true)
.deleteCookies(COOKIE_STRING))
.rememberMe( rememberMe -> rememberMe
.key(TOKEN_KEY)
.rememberMeParameter(REMEMBER_ME_KEY)
.tokenRepository(persistentTokenRepository())
.userDetailsService(userSvc)
.tokenValiditySeconds(validitySeconds));
The output:
[https-jsse-nio-7001-exec-10] [DEBUG] [o.s.security.web.FilterChainProxy] - Securing POST /logout
[https-jsse-nio-7001-exec-10] [DEBUG] [o.s.s.w.c.HttpSessionSecurityContextRepository] - Retrieved SecurityContextImpl [Authentication=UsernamePasswordAuthenticationToken [Principal=UserProfile [ blah blah, eventRoles=null, lastLoginDisplay=today, directoryUrl=null, groups=[]], Credentials=[PROTECTED], Authenticated=true, Details=WebAuthenticationDetails [RemoteIpAddress=127.0.0.1, SessionId=740230E1C02B57BEF504C23570FFA9EC], Granted Authorities=[]]]
[https-jsse-nio-7001-exec-10] [DEBUG] [o.s.s.w.c.SecurityContextPersistenceFilter] - Set SecurityContextHolder to SecurityContextImpl [Authentication=UsernamePasswordAuthenticationToken [Principal=UserProfile [blah blah eventRoles=null, lastLoginDisplay=today, directoryUrl=null, groups=[]], Credentials=[PROTECTED], Authenticated=true, Details=WebAuthenticationDetails [RemoteIpAddress=127.0.0.1, SessionId=740230E1C02B57BEF504C23570FFA9EC], Granted Authorities=[]]]
[https-jsse-nio-7001-exec-10] [DEBUG] [o.s.s.w.a.logout.LogoutFilter] - Logging out [UsernamePasswordAuthenticationToken [Principal=UserProfile [blah blah, eventRoles=null, lastLoginDisplay=today, directoryUrl=null, groups=[]], Credentials=[PROTECTED], Authenticated=true, Details=WebAuthenticationDetails [RemoteIpAddress=127.0.0.1, SessionId=740230E1C02B57BEF504C23570FFA9EC], Granted Authorities=[]]]
[https-jsse-nio-7001-exec-10] [DEBUG] [o.s.s.w.a.r.PersistentTokenBasedRememberMeServices] - Logout of user username
[https-jsse-nio-7001-exec-10] [DEBUG] [o.s.s.w.a.r.PersistentTokenBasedRememberMeServices] - Cancelling cookie
[https-jsse-nio-7001-exec-10] [DEBUG] [o.s.jdbc.core.JdbcTemplate] - Executing prepared SQL update
[https-jsse-nio-7001-exec-10] [DEBUG] [o.s.jdbc.core.JdbcTemplate] - Executing prepared SQL statement [delete from persistent_logins where username = ?]
[https-jsse-nio-7001-exec-10] [DEBUG] [o.s.jdbc.datasource.DataSourceUtils] - Fetching JDBC Connection from DataSource
[https-jsse-nio-7001-exec-10] [DEBUG] [o.s.s.w.a.l.SecurityContextLogoutHandler] - Invalidated session F3BC69312EC05C175A0FCEC298B49D06
[https-jsse-nio-7001-exec-10] [DEBUG] [o.s.s.w.c.HttpSessionSecurityContextRepository] - Did not store empty SecurityContext
[https-jsse-nio-7001-exec-10] [DEBUG] [o.s.s.w.c.HttpSessionSecurityContextRepository] - Did not store empty SecurityContext
[https-jsse-nio-7001-exec-10] [DEBUG] [o.s.s.w.c.SecurityContextPersistenceFilter] - Cleared SecurityContextHolder to complete request
Browser is never redirected to the loginSuccessUrl(), all DEBUG logging ends above. CSRF is enabled and logout was submitted POST.
I had .permitAll() in the logout configuration, but it seemed to have no effect in this case.
Screenshot showing request / response
Spring Security provides two implementations of LogoutSuccessHandler, see LogoutSuccessHandler:
LogoutSuccessHandler
The LogoutSuccessHandler is called after a successful logout by the LogoutFilter, to handle (for example) redirection or forwarding to the appropriate destination. Note that the interface is almost the same as the LogoutHandler but may raise an exception.
Spring Security provides the following implementations:
SimpleUrlLogoutSuccessHandler
HttpStatusReturningLogoutSuccessHandler
As mentioned earlier, you need not specify the SimpleUrlLogoutSuccessHandler directly. Instead, the fluent API provides a shortcut by setting the logoutSuccessUrl(). This sets up the SimpleUrlLogoutSuccessHandler under the covers. The provided URL is redirected to after a logout has occurred. The default is /login?logout.
The HttpStatusReturningLogoutSuccessHandler can be interesting in REST API type scenarios. Instead of redirecting to a URL upon the successful logout, this LogoutSuccessHandler lets you provide a plain HTTP status code to be returned. If not configured, a status code 200 is returned by default.
If you configure HTTP basic authentication, the HttpStatusReturningLogoutSuccessHandler is added, see
Default Logout Handler with HTTP Basic and XMLHttpRequest should be 204:
The default behavior for logout when the header X-Requested-With: XMLHttpRequest and HTTP Basic is enabled should be a 201. This will improve AngularJS experience which sends an accept header which includes text/html
See also: SEC-3103: Logout Success Content Negotiation.
Related
I have a SpringBoot WebFlux application that uses Netty and Spring Security. In my security config, I have
.logout().logoutUrl("/logout").logoutSuccessHandler(new HttpStatusReturningServerLogoutSuccessHandler())
and I can see that the security context is removed upon a POST to /logout
2022-05-31 11:52:05.700 DEBUG 37892 --- [or-http-epoll-3] o.s.s.w.s.a.logout.LogoutWebFilter : Logging out user 'AnonymousAuthenticationToken [Principal=anonymous, Credentials=[PROTECTED], Authenticated=true, Details=null, Granted Authorities=[ROLE_ANONYMOUS]]' and transferring to logout destination
2022-05-31 11:52:05.700 DEBUG 37892 --- [or-http-epoll-3] ebSessionServerSecurityContextRepository : Removed SecurityContext stored in WebSession: 'org.springframework.web.server.session.InMemoryWebSessionStore$InMemoryWebSession#1e33c2b6'
but the response infuriatingly contains a new Set-Cookie header for a new Session Cookie! Is this coming from Netty? How can I turn this off? Why is there no session configuration option for ServerHttpSecurity, where I could tell it to make new session only if required?
Any help appreciated, thanks
I figured out that the sessionId is changed in WebSessionServerSecurityContextRepository when the context is removed, but if I add
.logoutHandler(new WebSessionServerLogoutHandler())
to the configuration then it removes the SESSION cookie completely. Hopefully this helps someone else.
Can't find the way how to implement RememberMe service in OpenID. Have a class that extends AbstractRememberMeServices and it works with simple form login. Have next logs after login:
2015-12-17 07:29:15.086 DEBUG 260 --- [nio-8080-exec-5] p.s.s.CustomPersistentRememberMeServices : Did not send remember-me cookie (principal did not set parameter 'remember-me')
2015-12-17 07:29:15.086 DEBUG 260 --- [nio-8080-exec-5] p.s.s.CustomPersistentRememberMeServices : Remember-me login not requested.
Login works through /openid?openid_identifier=IDENTIFIER
Just add setAlwaysRemember(true); to your AbstractRememberMeServices implementation's constructor.
I am trying to implement OAuth2 into my Spring Boot app. When making a call to /oauth/token using a password grant, I am able to authenticate using Basic auth with a username/password against a database. The authentication object is loaded into the security context and the filter continues. However I noticed that further down the line the OAuth2AuthenticationProcessingFilter is called and cleans out the security context b/c no OAuth bearer token is found. This ultimately results in an authentication failure at the TokenEndpoint.postAccessToken method (e.g. /oauth/token).
Is it expected that the OAuth2AuthenticationProcessingFilter would be called during a /oauth/token call? If not, any ideas why? If it is expected what is the best fix/work around for this issue, to set stateless on the OAuth2AuthenticationProcessingFilter to false? Not sure if that is a good idea or not...
Some relevant info, I am using xml config for my spring security settings and JavaConfig for the rest. The grant type is password on the /oauth/token call.
Any help is greatly appreciated. Below are some log messages for context on what I am seeing.
09/30/15 22:42:50.899 [http-nio-9931-exec-8] DEBUG o.s.s.o.p.c.ClientCredentialsTokenEndpointFilter - Authentication success. Updating SecurityContextHolder to contain: org.springframework.security.oauth2.provider.OAuth2Authentication#9d98054f: Principal: testUser; Credentials: [PROTECTED]; Authenticated: true; Details: org.springframework.security.web.authentication.WebAuthenticationDetails#b364: RemoteIpAddress: 0:0:0:0:0:0:0:1; SessionId: null; Granted Authorities: ROLE_USER
09/30/15 22:42:50.899 [http-nio-9931-exec-8] DEBUG o.s.s.o.p.a.BearerTokenExtractor - Token not found in headers. Trying request parameters.
09/30/15 22:42:50.899 [http-nio-9931-exec-8] DEBUG o.s.s.o.p.a.BearerTokenExtractor - Token not found in request parameters. Not an OAuth2 request.
09/30/15 22:42:50.899 [http-nio-9931-exec-8] DEBUG o.s.s.o.p.a.OAuth2AuthenticationProcessingFilter - Clearing security context.
09/30/15 22:42:50.900 [http-nio-9931-exec-8] DEBUG o.s.s.o.p.a.OAuth2AuthenticationProcessingFilter - No token in request, will continue chain.
09/30/15 22:42:50.900 [http-nio-9931-exec-8] DEBUG o.s.s.o.p.a.BearerTokenExtractor - Token not found in headers. Trying request parameters.
09/30/15 22:42:50.900 [http-nio-9931-exec-8] DEBUG o.s.s.o.p.a.BearerTokenExtractor - Token not found in request parameters. Not an OAuth2 request.
09/30/15 22:42:50.900 [http-nio-9931-exec-8] DEBUG o.s.s.o.p.a.OAuth2AuthenticationProcessingFilter - No token in request, will continue chain.
09/30/15 22:42:50.934 [http-nio-9931-exec-8] DEBUG o.s.s.o.p.e.FrameworkEndpointHandlerMapping - Looking up handler method for path /oauth/token
09/30/15 22:42:50.937 [http-nio-9931-exec-8] DEBUG o.s.s.o.p.e.FrameworkEndpointHandlerMapping - Returning handler method [public org.springframework.http.ResponseEntity<org.springframework.security.oauth2.common.OAuth2AccessToken> org.springframework.security.oauth2.provider.endpoint.TokenEndpoint.postAccessToken(java.security.Principal,java.util.Map<java.lang.String, java.lang.String>) throws org.springframework.web.HttpRequestMethodNotSupportedException]
09/30/15 22:42:50.958 [http-nio-9931-exec-8] INFO o.s.s.o.p.endpoint.TokenEndpoint - Handling error: InsufficientAuthenticationException, There is no client authentication. Try adding an appropriate authentication filter.
09/30/15 22:42:51.070 [http-nio-9931-exec-8] DEBUG o.s.s.w.a.ExceptionTranslationFilter - Chain processed normally
09/30/15 22:42:51.070 [http-nio-9931-exec-8] DEBUG o.s.s.w.c.SecurityContextPersistenceFilter - SecurityContextHolder now cleared, as request processing completed
I am not able to find out where and when exactly the authentication manager is executed by spring security. I mean there are certian filters which are executed sequentially as below:
FIRST
- CHANNEL_FILTER
- CONCURRENT_SESSION_FILTER
- SECURITY_CONTEXT_FILTER
- LOGOUT_FILTER
- X509_FILTER
- PRE_AUTH_FILTER
- CAS_FILTER
- FORM_LOGIN_FILTER
- OPENID_FILTER
- BASIC_AUTH_FILTER
- SERVLET_API_SUPPORT_FILTER
- REMEMBER_ME_FILTER
- ANONYMOUS_FILTER
- EXCEPTION_TRANSLATION_FILTER
- SESSION_MANAGEMENT_FILTER
- FILTER_SECURITY_INTERCEPTOR
- SWITCH_USER_FILTER
- LAST
But when exactly authentication provider authenticates the provided username and password, i mean to ask after which these below filters is the authentication provider is executed .
Regards
Jayendra
From Spring Security documentation:
The order that filters are defined in the chain is very important.
Irrespective of which filters you are actually using, the order should
be as follows:
ChannelProcessingFilter, because it might need to redirect to a different protocol
SecurityContextPersistenceFilter, so a SecurityContext can be set up in the SecurityContextHolder at the beginning of a web request, and
any changes to the SecurityContext can be copied to the HttpSession
when the web request ends (ready for use with the next web request)
ConcurrentSessionFilter, because it uses the SecurityContextHolder functionality but needs to update the SessionRegistry to reflect
ongoing requests from the principal
Authentication processing mechanisms - UsernamePasswordAuthenticationFilter, CasAuthenticationFilter,
BasicAuthenticationFilter etc - so that the SecurityContextHolder can
be modified to contain a valid Authentication request token
The SecurityContextHolderAwareRequestFilter, if you are using it to install a Spring Security aware HttpServletRequestWrapper into your
servlet container
RememberMeAuthenticationFilter, so that if no earlier authentication processing mechanism updated the SecurityContextHolder,
and the request presents a cookie that enables remember-me services to
take place, a suitable remembered Authentication object will be put
there
AnonymousAuthenticationFilter, so that if no earlier authentication processing mechanism updated the SecurityContextHolder,
an anonymous Authentication object will be put there
ExceptionTranslationFilter, to catch any Spring Security exceptions so that either an HTTP error response can be returned or an
appropriate AuthenticationEntryPoint can be launched
FilterSecurityInterceptor, to protect web URIs and raise exceptions when access is denied
So the authentication manager is called at step 4. If you look at the source code of UsernamePasswordAuthenticationFilter you will see something like:
public Authentication attemptAuthentication(HttpServletRequest request,
HttpServletResponse response) throws AuthenticationException {
// ...
return this.getAuthenticationManager().authenticate(authRequest);
}
I am stuck with a problem and I can't figure out what is happening...
I have built an rest-api using spring integration. When I deploy the app in my local tomcat 7 everything works OK.
When I deploy it in mi bitnami stack (same tomcat version) and try the same request (changing the host), authentication step is OK but no mapping is found...
Basic authentication wih Spring Security is well done
2013-07-02 12:12:04,228 DEBUG intercept.FilterSecurityInterceptor -
Previously Authenticated:
org.springframework.security.authentication.UsernamePasswordAuthenticationToken#95f956f:
Principal: org.springframework.security.core.userdetails.User#36ebcb:
Username: user; Password: [PROTECTED]; Enabled: true;
AccountNonExpired: true; credentialsNonExpired: true;
AccountNonLocked: true; Granted Authorities: ROLE;
Credentials: [PROTECTED]; Authenticated: true; Details:
org.springframework.security.web.authentication.WebAuthenticationDetails#0:
RemoteIpAddress: 85.136.69.37; SessionId: null; Granted Authorities:
ROLE 2013-07-02 12:12:04,228 DEBUG
vote.AffirmativeBased - Voter:
org.springframework.security.web.access.expression.WebExpressionVoter#346946d5,
returned: 1 2013-07-02 12:12:04,228 DEBUG
intercept.FilterSecurityInterceptor - Authorization successful
Spring Integration mapping is not found
2013-07-02 12:12:04,228 DEBUG intercept.FilterSecurityInterceptor -
RunAsManager did not change Authentication object 2013-07-02
12:12:04,228 DEBUG web.FilterChainProxy -
/ptgapi/v1/clients/1/events/400 reached end of additional filter
chain; proceeding with original chain 2013-07-02 12:12:04,228 DEBUG
support.OpenEntityManagerInViewFilter - Opening JPA EntityManager in
OpenEntityManagerInViewFilter 2013-07-02 12:12:04,229 DEBUG
servlet.DispatcherServlet - DispatcherServlet with name 'Destiny
Customer Information Search Restful Web Service' processing GET request for [//PTG/ptgapi/v1/clients/1/events/400]
2013-07-02 12:12:04,229 WARN servlet.PageNotFound - No mapping
found for HTTP request with URI
[//PTG/ptgapi/v1/clients/1/events/400]
in DispatcherServlet with name 'Destiny Customer Information Search
Restful Web Service'
Any idea what is happening?
Update
I have found that Spring Integration is using a double slash (//) in local and a simple slash (/) in bitnami being the same war.
Could be the way Spring Integration splits the URL (local environment is windows and bitnami is Ubuntu)?
LOCAL
2013-07-02 15:31:28,443 DEBUG servlet.DispatcherServlet - DispatcherServlet with name 'Destiny Customer Information Search Restful Web Service' processing GET request for [/PTG/ptgapi/v1/clients/1/events/400]
2013-07-02 15:31:28,449 DEBUG inbound.UriPathHandlerMapping - Matching patterns for request [/ptgapi/v1/clients/1/events/400] are [/ptgapi/{apiVersion}/clients/{clientId}/events/{eventId}]
BITNAMI
2013-07-02 13:37:45,469 DEBUG servlet.DispatcherServlet - DispatcherServlet with name 'Destiny Customer Information Search Restful Web Service' processing GET request for [//PTG/ptgapi/v1/clients/1/events/400]
2013-07-02 13:37:45,470 WARN servlet.PageNotFound - No mapping found for HTTP request with URI [//PTG/ptgapi/v1/clients/1/events/400] in DispatcherServlet with name 'Destiny Customer Information Search RestfulWeb Service'
I have found that changing servlet-mapping to this one avoid this strange behaviour:
<servlet-mapping>
<servlet-name>Destiny Customer Information Search Restful Web Service</servlet-name>
<url-pattern>/rootCommonPath/*</url-pattern>
</servlet-mapping>