Spring Security 3.0 : Basic Auth Prompt disappear - spring

I am using spring basic authentication with the following settings in my security xml:
<http use-expressions="true" create-session="never" >
<intercept-url pattern="/**" method="GET" access="isAuthenticated()" />
<intercept-url pattern="/**" method="POST" access="isAuthenticated()" />
<intercept-url pattern="/**" method="PUT" access="isAuthenticated()" />
<intercept-url pattern="/**" method="DELETE" access="isAuthenticated()" />
<http-basic />
</http>
If authentication fails, the browser pop ups a prompt window to renter the user name and password.
Is there any way to make that prompt not pop up at all ?

Most probable the page that is used for authentication failure is also protected. You can try manually to set the failure page to one that is not protected like
<access-denied-handler error-page="/login.jsp"/>
together with
<intercept-url pattern="/*login*" access="hasRole('ROLE_ANONYMOUS')"/>
or
<intercept-url pattern='/*login*' filters='none'/>
or you can use the auto-config='true' attribute of the http element that will fix that for you.See more here

I have also had the same problem for the REST API throwing login dialog in the browser. As you have told , when the browser sees the response header as
WWW-Authenticate: Basic realm="Spring Security Application
It will prompt with a basic authentication dialog.For REST API based login , this is not ideal. Here is how I did it.Define a custom authentication entry point and in the commence
set the header as "FormBased"
response.setHeader("WWW-Authenticate", "FormBased");
application-context.xml configuration below
<security:http create-session="never" entry-point-ref="authenticationEntryPoint" authentication-manager-ref="authenticationManager">
<security:custom-filter ref="customRestFilter" position="BASIC_AUTH_FILTER" />
<security:intercept-url pattern="/api/**" access="ROLE_USER" />
</security:http>
<bean id="authenticationEntryPoint" class="com.tito.demo.workflow.security.RestAuthenticationEntryPoint">
</bean>
Custom entry point class below.
#Component
public class RestAuthenticationEntryPoint implements AuthenticationEntryPoint {
private static Logger logger = Logger.getLogger(RestAuthenticationEntryPoint.class);
public void commence( HttpServletRequest request, HttpServletResponse response,
AuthenticationException authException ) throws IOException {
logger.debug("<--- Inside authentication entry point --->");
// this is very important for a REST based API login.
// WWW-Authenticate header should be set as FormBased , else browser will show login dialog with realm
response.setHeader("WWW-Authenticate", "FormBased");
response.setStatus( HttpServletResponse.SC_UNAUTHORIZED );
}
}
Note: I have used spring 3.2.5.Release
Now when the rest API is hit from a restclient like POSTMAN , the server will return 401 Unauthorized.

I have faced the same issue and what I did is create a custom RequestMatcher in my resource server. This prevents Outh2 from sending WWW-Authenticate header.
Example:
#Override
public void configure(HttpSecurity http) throws Exception {
http.requestMatcher(new OAuthRequestedMatcher())
.authorizeRequests()
.antMatchers(HttpMethod.OPTIONS).permitAll()
.anyRequest().authenticated();
}
private static class OAuthRequestedMatcher implements RequestMatcher {
public boolean matches(HttpServletRequest request) {
String auth = request.getHeader("Authorization");
boolean haveOauth2Token = (auth != null) && auth.startsWith("Bearer");
boolean haveAccessToken = request.getParameter("access_token")!=null;
return haveOauth2Token || haveAccessToken;
}
}

Related

Spring Security with overlapping configurations checks authorization in multiple <http> blocks

I have an application using Spring Security 4.2.18 with the security configured in xml. Right now I am introducing Spring Boot 2.5.4 in the project. Since upgrading, I have a problem with the configuration of security for some requests.
I have a block matching specific requests and one matching the rest of all requests.
<http pattern="/api/**" use-expressions="true" authentication-manager-ref="apiAuthenticationManager" >
<http-basic/>
<intercept-url pattern="/api/**" access="hasRole('ROLE_API')"/>
<csrf disabled="true"/>
</http>
<http pattern="/**" auto-config="true" use-expressions="true" create-session="always" disable-url-rewriting="true"
authentication-manager-ref="authenticationManager">
<form-login login-processing-url="/resources/j_spring_security_check" login-page="/login"
username-parameter="j_username"
password-parameter="j_password"
authentication-failure-url="/login?login_error=t" default-target-url="/redirectlogin"/>
<logout logout-url="/resources/j_spring_security_logout"/>
...
<intercept-url pattern="/**" access="hasRole('ROLE_ADMIN')"/>
...
</http>
<authentication-manager id="apiAuthenticationManager">
<authentication-provider user-service-ref="apiUserDetailsService">
</authentication-provider>
</authentication-manager>
The ApiUserDetailsService follows the specification:
#Service
#Transactional
public class ApiUserDetailsService implements UserDetailsService {
...
#Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException, DataAccessException {
boolean foundAccount = apiConfig.getUsername().equals(username);
if (foundAccount) {
return new User(username, apiConfig.getPassword(), singletonList(new SimpleGrantedAuthority("ROLE_API")));
}
throw new UsernameNotFoundException("Could not finder user with name " + username);
}
}
If I make a request to something under /api and use incorrect Basic auth credentials, I previously got a 401 Unauthorized response. Since upgrading I get redirected to /login and end up in a redirect loop since the credentials are used there as well.
If I remove the <intercept-url pattern="/**" access="hasRole('ROLE_ADMIN')"/> in the second <http> block, I get the expected behaviour.
How can I configure my application so that my request doesn't try to authorize using the rules in the second <http> block? I have tried using an EntryPoint but it isn't called before the user is erroneously authorized using the method of the generic <http> block.
Have you tried putting <intercept-url pattern="/login*" access="permitAll" /> right above your <intercept-url pattern="/**" access="hasRole('ROLE_ADMIN')"/> like:
In the second http block:
...
<intercept-url pattern="/login*" access="permitAll" />
<intercept-url pattern="/**" access="hasRole('ROLE_ADMIN')"/>
...
The problem wasn't that the configurations were overlapping but rather that the failed authentication was redirected to /login which in turn was handled by the second <http> configuration.
I solved it by creating an implementation of AuthenticationEntryPoint that sets the 401 Unathorized status on the response:
#Component
public class BasicAuthenticationEntryPoint implements AuthenticationEntryPoint {
#Override
public void commence(HttpServletRequest request, HttpServletResponse response, AuthenticationException authException) {
response.setStatus(HttpStatus.UNAUTHORIZED.value());
}
}
and in turn referencing to this in my basic authentication configuration:
<http pattern="/api/**" use-expressions="true" authentication-manager-ref="apiAuthenticationManager" >
<http-basic entry-point-ref="basicAuthenticationEntryPoint"/>
<intercept-url pattern="/api/**" access="hasRole('ROLE_API')"/>
<csrf disabled="true"/>
</http>

How to configure one login page with Multiple landing pages which intercept with different url patterns in spring security 4

My requirement is, I have two different landing pages one for user and one for admin. This landing pages has to be appear based on the intercept url pattern which i configure in the spring security xml file. Both the landing page is having a hyperlink to login, when the user click on the login hyperlink of adminLayout.jsp it will load the same login page and when the user click on the login hyperlink of userLayout.jsp it will load the same login page by interacting with the controller for two different url patterns.The url patterns will be /admin and /user.
I stuck here.
How can i configure two different landing pages(adminLayout) and userLayout) in spring security. this tow landing pages is having the same login form, which i want to configure in the spring security form-login for both the url patterns and tow layouts.Before login only the landing page has to appear and then when the user click on the login hyperlink from two different pages it has to make user of spring security provided login-form functionality.Please help me out on this.
<http auto-config="true" use-expressions="true">
<intercept-url pattern="/admin**" access="hasRole('ROLE_ADMIN')" requires-channel="http" />
<intercept-url pattern="/user**" access="hasRole('ROLE_USER')" requires-channel="http" />
<csrf disabled="true"/>
<access-denied-handler ref="accessDeniedHandler"/>
<!-- Here i want to configure the landing pages based on the intercept url pattern if the pattern is /admin i want to dispaly the adminLayout.
If the intercept url pattern is /user i want to display the userLayout. Both this Layout pages is having common login page which user will click from this layout pages.
if want to make use of spring secuiryt for form login..
-->
<form-login login-processing-url="/j_spring_security_check"
login-page="/sslogin" authentication-success-handler-ref="authenticationHandler"
authentication-failure-url="/fail2login?error=true"/>
<logout logout-success-url="/logout" invalidate-session="true" logout-url="/j_spring_security_logout" delete-cookies="JSESSIONID" />
<session-management>
<concurrency-control error-if-maximum-exceeded="true" max-sessions="1" expired-url="/fail2login" />
</session-management>
</http>
<beans:bean id="accessDeniedHandler" class="com.fss.portal.handlers.PortalAccessDeniedHandler">
<beans:property name="accessDeniedURL" value="403"></beans:property>
</beans:bean>
<beans:bean id="authenticationHandler" class="com.fss.portal.handlers.AuthenticationHandler">
</beans:bean>
<beans:bean id="customAuthenticationProvider" class="com.fss.portal.utility.CustomAuthenticationProvider">
<beans:property name="passwordEncoder" ref="bcryptEncoder"></beans:property>
</beans:bean>
<beans:bean id="bcryptEncoder" class="org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder" />
<authentication-manager>
<authentication-provider ref="customAuthenticationProvider">
</authentication-provider>
</authentication-manager>
I think you should try creating a AuthenticationEntryPoint implementation with multiple landing page support.
It could be something like this:
import java.util.Map;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.web.AuthenticationEntryPoint;
import org.springframework.security.web.authentication.LoginUrlAuthenticationEntryPoint;
import org.springframework.security.web.util.matcher.RegexRequestMatcher;
import org.springframework.security.web.util.matcher.RequestMatcher;
public class MultipleLandingPageEntryPoint extends LoginUrlAuthenticationEntryPoint
implements AuthenticationEntryPoint {
private Map<String, String> landingPages;
public MultipleLandingPageEntryPoint(String defaultLoginFormUrl, Map<String, String> landingPages) {
super(defaultLoginFormUrl);
this.landingPages = landingPages;
}
public MultipleLandingPageEntryPoint(String defaultLoginFormUrl) {
super(defaultLoginFormUrl);
}
public Map<String, String> getLandingPages() {
return landingPages;
}
public void setLandingPages(Map<String, String> landingPages) {
this.landingPages = landingPages;
}
#Override
protected String determineUrlToUseForThisRequest(HttpServletRequest request, HttpServletResponse response,
AuthenticationException exception) {
for(String key : this.landingPages.keySet()){
RequestMatcher rm = new RegexRequestMatcher(key, null);
if(rm.matches(request)){
return this.landingPages.get(key);
}
}
// If not found in the map, return the default landing page through superclass
return super.determineUrlToUseForThisRequest(request, response, exception);
}
}
Then, in your security config, you must configure it:
<beans:bean id="authenticationMultiEntryPoint" class="com.xxx.yyy.MultipleLandingPageEntryPoint">
<beans:constructor-arg value="/user/landing.htm" />
<beans:property name="landingPages">
<beans:map>
<beans:entry key="/user**" value="/user/landing.htm" />
<beans:entry key="/admin**" value="/admin/landing.htm" />
</beans:map>
</beans:property>
</beans:bean>
And use it in your <security:http> element:
<security:http pattern="/admin/landing.htm" security="none" />
<security:http pattern="/user/landing.htm" security="none" />
<security:http auto-config="true" use-expressions="true"
entry-point-ref="authenticationMultiEntryPoint">
If you implement the AuthenticationEntryPoint extending LoginUrlAuthenticationEntryPoint (which I think it's a good idea) check additional parameters on it.
EDIT: I've just updated the class implementation, did not include the latest version
Create A Custom AuthenticationSuccessHandler like below
public class CustomAuthenticationSuccessHandler implements AuthenticationSuccessHandler {
public void onAuthenticationSuccess(javax.servlet.http.HttpServletRequest request,
javax.servlet.http.HttpServletResponse response,
Authentication authentication)
throws IOException,
javax.servlet.ServletException {
if(authentication.getAuthorities().contains(new SimpleGrantedAuthority("ROLE_ADMIN")) {
request.getRequestDispatcher("/admin").forward(request, response);
} else if (authentication.getAuthorities().contains(new SimpleGrantedAuthority("ROLE_USER")) {
request.getRequestDispatcher("/user").forward(request, response);
}
}
}
And configure it with form-login tag as following
<bean id="customAuthenticationSuccessHandler" class="CustomAuthenticationSuccessHandler" />
<form-login authentication-success-handler-ref="customAuthenticationSuccessHandler" ...>
UPDATE
Create a Controller mappings /landing point to it by <form-login login-page="/landing" .../>. This landing should have links to admin and user landing pages. Which can have links or forms to login.
You can remove protection from these landing pages.
<http pattern="/landing**" security="none"/>
<http pattern="/landing/admin**" security="none"/>
<http pattern="/landing/user**" security="none"/>
And you can write a Custom AuthenticationFailureHandler to redirect to correct login page.

Secure rest api using spring security - custom status codes

I am using spring security (4.0.0.M2) to secure my web application and also my rest api. everything works great. but I have one problem. I want to return http result 403 (forbidden) instean of 401, when user could not be authenticated
I defined different http definitions, for each authentication scenario, one for web and one for api.
<http pattern="/rest/v?/**" auto-config="false" use-expressions="true"
disable-url-rewriting="true" authentication-manager-ref="tokenAuthenticationManager"
create-session="stateless" realm="API security check"
entry-point-ref="http403EntryPoint">
<intercept-url pattern="/rest/v?/*" access="isAuthenticated()" />
<anonymous enabled="false" />
<http-basic />
</http>
<authentication-manager id="tokenAuthenticationManager" erase-credentials="true">
<authentication-provider user-service-ref="tokenUserDetailsService" />
</authentication-manager>
public class TokenUserDetailsService implements UserDetailsService {
#Override
public org.springframework.security.core.userdetails.UserDetails loadUserByUsername(String checkUser)
throws UsernameNotFoundException {
// lookup user
if (user == null) {
// here I whant to return 403 instead of 401
throw new UsernameNotFoundException("User not found");
}
}
}
Could somebody help to return http status code 403 in this case?
Thank you for help.
Best regards
sonny
The BasicAuthenticationFilter which is created by the <http-basic> element also has a reference to an entry point. It invokes this when authentication fails and this is where the 401 code comes from (which is normal with Basic authentication).
If you want to change it you can use the entry-point-ref namespace attribute. So
<http-basic entry-point-ref="http403EntryPoint" />
should give the result you want.
Currently I found out a working solution. When I use a custom filter (position=pre_auth), then I could change the response code using doFilter method.
But I am not sure, if this is really the best solution.

Spring Security 3.2 CSRF disable for specific URLs

Enabled CSRF in my Spring MVC application using Spring security 3.2.
My spring-security.xml
<http>
<intercept-url pattern="/**/verify" requires-channel="https"/>
<intercept-url pattern="/**/login*" requires-channel="http"/>
...
...
<csrf />
</http>
Trying to disable CSRF for requests that contain 'verify' in request URL.
MySecurityConfig.java
#Configuration
#EnableWebSecurity
public class MySecurityConfig extends WebSecurityConfigurerAdapter {
private CsrfMatcher csrfRequestMatcher = new CsrfMatcher();
#Override
public void configure(HttpSecurity http) throws Exception {
http.csrf().requireCsrfProtectionMatcher(csrfRequestMatcher);
}
class CsrfMatcher implements RequestMatcher {
#Override
public boolean matches(HttpServletRequest request) {
if (request.getRequestURL().indexOf("verify") != -1)
return false;
else if (request.getRequestURL().indexOf("homePage") != -1)
return false;
return true;
}
}
}
Csrf filter validates CSRF token that is submitted from 'verify' and Invalid token exception (403) is thrown as I'm submitting request to https from http. How can I disable csrf token authentication in such a scenario ?
I know this is not a direct answer, but people (as me) usually don't specify spring's version when searching for this kinds of questions.
So, since spring security a method exists that lets ignore some routes:
The following will ensure CSRF protection ignores:
Any GET, HEAD, TRACE, OPTIONS (this is the default)
We also explicitly state to ignore any request that starts with "/sockjs/"
http
.csrf()
.ignoringAntMatchers("/sockjs/**")
.and()
...
I hope that my answer can help someone else. I found this question searching for How to disable CSFR for specfic URLs in Spring Boot.
I used the solution described here:
http://blog.netgloo.com/2014/09/28/spring-boot-enable-the-csrf-check-selectively-only-for-some-requests/
This is the Spring Security configuration that allow me to disable the CSFR control on some URLs:
#Configuration
#EnableWebMvcSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
#Override
protected void configure(HttpSecurity http) throws Exception {
// Build the request matcher for CSFR protection
RequestMatcher csrfRequestMatcher = new RequestMatcher() {
// Disable CSFR protection on the following urls:
private AntPathRequestMatcher[] requestMatchers = {
new AntPathRequestMatcher("/login"),
new AntPathRequestMatcher("/logout"),
new AntPathRequestMatcher("/verify/**")
};
#Override
public boolean matches(HttpServletRequest request) {
// If the request match one url the CSFR protection will be disabled
for (AntPathRequestMatcher rm : requestMatchers) {
if (rm.matches(request)) { return false; }
}
return true;
} // method matches
}; // new RequestMatcher
// Set security configurations
http
// Disable the csrf protection on some request matches
.csrf()
.requireCsrfProtectionMatcher(csrfRequestMatcher)
.and()
// Other configurations for the http object
// ...
return;
} // method configure
#Autowired
public void configureGlobal(AuthenticationManagerBuilder auth)
throws Exception {
// Authentication manager configuration
// ...
}
}
It works with Spring Boot 1.2.2 (and Spring Security 3.2.6).
I am using Spring Security v4.1. After a lot of reading and testing, I disable the CSRF security feature for specific URLs using XML configuration.
<beans:beans xmlns="http://www.springframework.org/schema/security"
xmlns:beans="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:util="http://www.springframework.org/schema/util"
xsi:schemaLocation="
http://www.springframework.org/schema/security http://www.springframework.org/schema/security/spring-security-4.1.xsd
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.1.xsd
http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util-4.0.xsd">
<http pattern="/files/**" security="none" create-session="stateless"/>
<http>
<intercept-url pattern="/admin/**" access="hasAuthority('GenericUser')" />
<intercept-url pattern="/**" access="permitAll" />
<form-login
login-page="/login"
login-processing-url="/login"
authentication-failure-url="/login"
default-target-url="/admin/"
password-parameter="password"
username-parameter="username"
/>
<logout delete-cookies="JSESSIONID" logout-success-url="/login" logout-url="/admin/logout" />
<http-basic />
<csrf request-matcher-ref="csrfMatcher"/>
</http>
<beans:bean id="csrfMatcher" class="org.springframework.security.web.util.matcher.OrRequestMatcher">
<beans:constructor-arg>
<util:list value-type="org.springframework.security.web.util.matcher.RequestMatcher">
<beans:bean class="org.springframework.security.web.util.matcher.AntPathRequestMatcher">
<beans:constructor-arg name="pattern" value="/rest/**"/>
<beans:constructor-arg name="httpMethod" value="POST"/>
</beans:bean>
<beans:bean class="org.springframework.security.web.util.matcher.AntPathRequestMatcher">
<beans:constructor-arg name="pattern" value="/rest/**"/>
<beans:constructor-arg name="httpMethod" value="PUT"/>
</beans:bean>
<beans:bean class="org.springframework.security.web.util.matcher.AntPathRequestMatcher">
<beans:constructor-arg name="pattern" value="/rest/**"/>
<beans:constructor-arg name="httpMethod" value="DELETE"/>
</beans:bean>
</util:list>
</beans:constructor-arg>
</beans:bean>
//...
</beans:bean>
With the above configuration, I enable the CSRF security only for POST|PUT|DELETE requests of all URLs which start with /rest/.
Explicitly disable for specific url patterns and enable for some url patterns.
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.annotation.Order;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
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;
#EnableWebSecurity
public class SecurityConfig {
#Configuration
#Order
public static class GeneralWebSecurityConfig extends WebSecurityConfigurerAdapter {
protected void configure(HttpSecurity http) throws Exception {
http.csrf().ignoringAntMatchers("/rest/**").and()
.authorizeRequests()
.antMatchers("/").permitAll()
.antMatchers("/home/**","/search/**","/geo/**").authenticated().and().csrf()
.and().formLogin().loginPage("/login")
.usernameParameter("username").passwordParameter("password")
.and().exceptionHandling().accessDeniedPage("/error")
.and().sessionManagement().maximumSessions(1).maxSessionsPreventsLogin(true);
}
}
}
<http ...>
<csrf request-matcher-ref="csrfMatcher"/>
<headers>
<frame-options policy="SAMEORIGIN"/>
</headers>
...
</http>
<b:bean id="csrfMatcher"
class="AndRequestMatcher">
<b:constructor-arg value="#{T(org.springframework.security.web.csrf.CsrfFilter).DEFAULT_CSRF_MATCHER}"/>
<b:constructor-arg>
<b:bean class="org.springframework.security.web.util.matcher.NegatedRequestMatcher">
<b:bean class="org.springframework.security.web.util.matcher.AntPathRequestMatcher">
<b:constructor-arg value="/chat/**"/>
</b:bean>
</b:bean>
</b:constructor-arg>
</b:bean>
mean of
http
.csrf()
// ignore our stomp endpoints since they are protected using Stomp headers
.ignoringAntMatchers("/chat/**")
example from :
https://docs.spring.io/spring-security/site/docs/4.1.x/reference/htmlsingle/
Use security="none".
for e.g in spring-security-config.xml
<security:intercept-url pattern="/*/verify" security="none" />

Spring Security custom LogoutSuccessHandler gets strange Authentication object

I am developing an application using the Spring Security (3.1) and I encoutered the following problem. When user logs out, I want to redirect to some custom URL depending if he logs out from a secure page or not. I wrote a custom LogoutHandler, that looks as follow:
#Override
public void onLogoutSuccess(HttpServletRequest request,
HttpServletResponse response, Authentication authentication)
throws IOException, ServletException {
String refererUrl = request.getHeader("Referer");
if (requiredAuthentication(refererUrl, authentication)) {
response.sendRedirect(request.getContextPath());
} else {
response.sendRedirect(refererUrl);
}
}
private boolean requiredAuthentication(String url, Authentication authentication){
return !getPrivilegeEvaluator().isAllowed(url, authentication);
}
So, when the user is logging out from the non-secure page he is logged out and redirected to the same URL, and if he is logging ouf from secure page, he goes to index page.
The problem is, that Authentication object that comes to the method is always authenticated (even though, the method is called AFTER loggin out the user, acording to the specification).
My security context:
<http use-expressions="true" disable-url-rewriting="true" request-matcher-ref="requestMatcher" >
<intercept-url pattern="/admin/**" access="hasRole('ROLE_ADMIN')" requires-channel="https" />
<intercept-url pattern="/dashboard/**" access="hasRole('ROLE_OWNER')" requires-channel="https" />
<intercept-url pattern="/**" access="permitAll"/>
<form-login login-page="/login"
authentication-success-handler-ref="successHandler"
authentication-failure-url="/login"
login-processing-url="/validate" />
<logout logout-url="/logout" invalidate-session="true" success-handler-ref="logoutSuccessHandler" />
<remember-me services-ref="rememberMeServices" key="KEY" use-secure-cookie="false" />
<session-management session-fixation-protection="migrateSession">
<concurrency-control max-sessions="1" />
</session-management>
</http>
Do you have any idea, why received Authentication is still valid, when gettig to the logoutSuccessHandler? I can't edit this object, because it's fields are final (except the isAuthenticated, but it's not checked by isAllowed() method..)
Looking at Spring Security source code, the LogoutFilter gets the Authentication object from the SecurityContextHolder, keeps it on a local variable, and removes it from the holder, via SecurityContextLogoutHandler. After all LogoutHandlers are called, it calls your LogoutSuccessHandler, and passes the Authentication object.
Even that it says it is valid, it is not anymore in the SecurityContextHolder, so for Spring, the user is logged out.

Resources