Well, I do not implement PersistentTokenBasedRememberMeServices therefore I cannot use .logout(request, response, auth). But I use JdbcTokenRepositoryImpl in order to use PersistentTokenRepository for remember-me feature.
LogoutController:
#Controller
public class LogoutController {
#RequestMapping(value = {"/logout"}, method = RequestMethod.GET)
public String logout() {
Authentication auth = SecurityContextHolder.getContext().getAuthentication();
if(auth != null) {
SecurityContextHolder.getContext().setAuthentication(null);
}
return "redirect:/login?logout";
}
}
Security config:
#Configuration
#EnableWebSecurity
public class AppSecurityConfig extends WebSecurityConfigurerAdapter {
#Autowired
private DataSource dataSource;
#Autowired
private UserDetailsService userDetailsService;
#Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.authenticationProvider(authProvider());
}
#Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests()
.antMatchers("/", "/playground").hasAnyRole("ROOT", "MODER", "USER")
.antMatchers("/users/**").hasAnyRole("ROOT", "MODER")
.and()
.formLogin().loginPage("/login").loginProcessingUrl("/login").failureHandler(customAuthenticationFailureHandler())
.and()
.rememberMe().rememberMeParameter("remember-me").tokenRepository(persistentTokenRepository()).userDetailsService(userDetailsService)
.and()
.logout().logoutUrl("/logout");
}
#Bean
public BCryptPasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
#Bean
public DaoAuthenticationProvider authProvider() {
DaoAuthenticationProvider authProvider = new DaoAuthenticationProvider();
authProvider.setPasswordEncoder(passwordEncoder());
authProvider.setUserDetailsService(userDetailsService);
return authProvider;
}
#Bean
public AuthenticationFailureHandler customAuthenticationFailureHandler() {
return new CustomAuthenticationFailureHandler();
}
#Bean
public PersistentTokenRepository persistentTokenRepository() {
JdbcTokenRepositoryImpl jdbcTokenRepository = new JdbcTokenRepositoryImpl();
jdbcTokenRepository.setDataSource(dataSource);
return jdbcTokenRepository;
}
When I log in with remember-me, I cannot log out then. I guess because of remember-me feature. What should I add to LogoutController to make a proper logout proccess?
Note: the thing is, that if I just use POST method on logout, then it perfectly works, but I'd like to use GET method and thus I have to create a logout controller to perform get method.
Try to disable crsf (http.csrf().disable()).
The default implementation in spring's security Logout filter is:
if (http.getConfigurer(CsrfConfigurer.class) != null) {
this.logoutRequestMatcher = new AntPathRequestMatcher(this.logoutUrl, "POST");
}
else {
this.logoutRequestMatcher = new OrRequestMatcher(
new AntPathRequestMatcher(this.logoutUrl, "GET"),
new AntPathRequestMatcher(this.logoutUrl, "POST"),
new AntPathRequestMatcher(this.logoutUrl, "PUT"),
new AntPathRequestMatcher(this.logoutUrl, "DELETE")
);
}
as you can see if your Csrf is enabled (by default it's enabled even if you overwride protected void configure(HttpSecurity http)) then only POST method will be work, if not all are working.
BTW: Are you sure your request reaching the LogoutController, because I thing it's uses standard spring security logout mechanism? (To disable it do http.logout().disable(), the same as csrf it's enabled by default)
To sum it up.
I've managed to test several ways and here what I have got:
As M. Deinum suggested in comments, it's possible not to use a controller and nevertheless have a logout with a GET request. Here it is.
.logout().logoutRequestMatcher(new AntPathRequestMatcher("/logout"))
As Andrew Sasha suggested in the answer section, you can disable csrf as it intentionally prevents using GET request. And now you can use a GET request to log out without even using any controller.
http.csrf().disable()
If you still wish to use the controller, none of the following things will help you
.deleteCookies("remember-me", "JSESSIONID")
.invalidateHttpSession(true)
.clearAuthentication(true)
.logoutSuccessUrl("/")
(I'm not sure, but I feel like it doesn't work because you perform a GET request and use your controller to control logout)
So then you can do it programmatically
First, you add a name for remember me cookie in Spring security config:
rememberMe().rememberMeCookieName("remember-me")
And then in logout controller add this:
String cookieName = "remember-me";
Cookie cookie = new Cookie(cookieName, null);
cookie.setMaxAge(0);
cookie.setPath(StringUtils.hasLength(request.getContextPath()) ? request.getContextPath() : "/");
response.addCookie(cookie);
The only problem here is that you have to delete a record from persistent_logins table manually
(In order to get a request and a response you just pass them into a method public void logout(HttpServletRequest request, HttpServletResponse response)
It is possible to use a POST request but use it as a link with the help of JavaScript or even plain HTML and CSS.
The solutions for that you can find on this topic.
So what do we have here?
Summarizing everything above I can say that if you want a controller, you have to programmatically write everything yourself (someone would tell that it's a reinventing of a wheel).
Still, it is possible to use a GET request but without controller which is described in the 1st and 2nd positions of the list.
(The consequences of using a GET request is written within CSRF Documentation and it does not recommend to use a GET request because of its invulnerability.)
So the last thing that I decided to be my favorite is to make a POST request look like a GET request (use it as a link) with the help of JS or HTML and CSS. And as you use a POST request you kind of have a CSRF protection.
I hope this will help someone.
Related
We are implementing a role-based security API (bearer-token only) with spring-boot and Keycloak.
The security config looks as follows:
#KeycloakConfiguration
public class SecurityConfig extends KeycloakWebSecurityConfigurerAdapter {
/**
* Registers the KeycloakAuthenticationProvider with the authentication manager.
*/
#Autowired
public void configureGlobal(AuthenticationManagerBuilder auth) {
auth.authenticationProvider(keycloakAuthenticationProvider());
}
#Bean
public KeycloakConfigResolver KeycloakConfigResolver() {
return new KeycloakSpringBootConfigResolver();
}
/**
* Defines the session authentication strategy.
* For bearer-only applications there is no session needed and therefor
* we use the NullAuthenticatedSessionStrategy.
*/
#Bean
#Override
protected SessionAuthenticationStrategy sessionAuthenticationStrategy() {
return new NullAuthenticatedSessionStrategy();
}
#Override
protected void configure(HttpSecurity http) throws Exception
{
super.configure(http);
http.authorizeRequests()
.anyRequest()
.permitAll();
}
}
I retrieve roles with
Authentication auth = SecurityContextHolder.getContext().getAuthentication();
if (auth instanceof KeycloakAuthenticationToken) {
SimpleKeycloakAccount user = (SimpleKeycloakAccount) auth.getDetails();
for (String role : user.getRoles()) {
// Clean and collect roles...
}
}
This works and we get the roles. The problem is, that we even get roles when no request header "Authorization: Bearer [token]" has been send. This happens, when we have two consecutive calls, the first with valid token, then the second call (without Authorization header) has the same KeycloakAuthenticationToken with the same roles.
My questions are now:
How can it be that the Authorization header is null, but the security context still returns a KeycloakAuthenticationToken?
Shouldn't the security context be per thread, and each thread exists during a single http request only?
Update
I found a workaround in the meantime, that is ugly but solves the problem for the moment. I've written a custom HandlerInterceptor, that cleans the security context, if no Authorization header can be found:
if (request.getHeader("Authorization") == null)
SecurityContextHolder.clearContext();
Although this seems to solve the issue, it shouldn't be necessary. So something is still strange, I guess.
I'm building an OAuth2 Authorization server that supports Restful API with Spring Authorization Server and Spring Security.
I want a SPA application built by React to provide a login interface at /login and submit the login information to the /api/login path with a Post request.
I extend UsernamePasswordauthenticationFilter to support Restful-style Post requests to parse Json data from body:
public class RestfulUsernamePasswordAuthenticationFilter extends UsernamePasswordAuthenticationFilter {
public RestfulUsernamePasswordAuthenticationFilter(AuthenticationManager authenticationManager) {
super(authenticationManager);
}
private String jsonUsername;
private String jsonPassword;
#Override
protected String obtainPassword(HttpServletRequest request) {
if (request.getContentType().equals(MediaType.APPLICATION_JSON_VALUE)) {
return this.jsonPassword;
} else {
return super.obtainPassword(request);
}
}
#Override
protected String obtainUsername(HttpServletRequest request) {
if (request.getContentType().equals(MediaType.APPLICATION_JSON_VALUE)) {
return this.jsonUsername;
} else {
return super.obtainUsername(request);
}
}
#Override
public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response)
throws AuthenticationException {
if ("application/json".equals(request.getHeader("Content-Type"))) {
try {
/*
* HttpServletRequest can be read only once
*/
//json transformation
Map<String, String> requestMap = new ObjectMapper().readValue(request.getInputStream(), Map.class);
this.jsonUsername = requestMap.get("username");
this.jsonPassword = requestMap.get("password");
} catch (Exception e) {
throw new AuthenticationServiceException(e.getMessage(), e);
}
}
return super.attemptAuthentication(request, response);
}
}
In the configuration, I replaced the custom RestfulUsernamePasswordauthenticationFilter with UsernamePasswordauthenticationFilter, and used .loginProcessUrl to set the path for processing Post requests to /api/login:
#Override
protected void configure(HttpSecurity http)
throws Exception {
http.addFilterAt(new RestfulUsernamePasswordAuthenticationFilter(authenticationManagerBean()),
UsernamePasswordAuthenticationFilter.class);
http.authorizeRequests()
.antMatchers("/api/all")
.permitAll()
.antMatchers("/api/auth")
.hasRole("USER")
.antMatchers("/api/admin")
.hasRole("ADMIN")
.antMatchers("/login", "/register", "/api/login")
.permitAll();
http.formLogin()
.loginPage("/login")
.loginProcessingUrl("/api/login");
http.csrf().disable();
}
The problem is that although I set the path to process Post requests through .loginProcessingUrl, it doesn't seem to work.
When a Post request is submitted to /api/login, it will be redirected to/login like all unauthenticated requests, and the request submitted to /login will take effect normally.
In the process of debugging, I found that .loginProcessingUrl will register this path in a UsernamePasswordconfirationFilter, but will not be processed by my custom RestfulUsernamePasswordShareationFilter. In the process of debugging, I found that .loginProcessingUrl will register this path in a UsernamePasswordFilter.
I want to know if there is any way to make .loginProcessingUrl work on my custom AuthenticationFilter.
At the same time, can I easily customize the path to which they accept requests when I add more custom Filter?
Maybe I will add more AuthenticationProvider that need to read Restful information in the future. how should I design the architecture of these Filter and Provider to make it easier to expand?
I think I solved the problem after read this blog. As it said:
After you provided your custom filter, you can no longer use the Spring HttpSecurity builder. If you still use it, you’ll configure the default Filter, not yours!
We tell Spring to use our implementatin by the “addFIlterBefore” function.
After this little modification, the test APIs work the same way, the only difference is that you should provide ‘login’ and ‘password’ params in the POST request body (and not the query string).
So I have to manually set the AntPathRequestMatcher withsetRequiresAuthenticationRequestMatcher of the filter class like this:
RestfulUsernamePasswordAuthenticationFilter restfulFilter = new RestfulUsernamePasswordAuthenticationFilter(
this.authenticationManagerBean());
restfulFilter.setRequiresAuthenticationRequestMatcher(
new AntPathRequestMatcher("/api/login", "POST"));
http.addFilterBefore(restfulFilter,
UsernamePasswordAuthenticationFilter.class);
By this way, you can configure the path to handle the POST request just like you use loginProcessingUrl in the formal way.
You can also use same method to add more custom filter and authenticationProvider in the future, just configure them manually.
I'm looking for the proper way to add role based authentication where I extract out roles from a JWT.
Ideally, I would like to extract roles from the JWT after authentication has taken place. This will work by inspecting the web token for some fields related to the roles which we are getting from our authentication system, keycloak.
My question is: is it possible to append roles to a request and then use http configuration to require one of these extracted roles?
Below is some relevant code that will help explain what I'm doing.
In my WebSecurityConfigurer, I make the access token available, scoped per request.
#Bean
#Scope(scopeName = WebApplicationContext.SCOPE_REQUEST, proxyMode = ScopedProxyMode.TARGET_CLASS)
public AccessToken accessToken() {
try {
HttpServletRequest request =
((ServletRequestAttributes) RequestContextHolder.currentRequestAttributes())
.getRequest();
return ((KeycloakSecurityContext) ((KeycloakAuthenticationToken) request
.getUserPrincipal())
.getCredentials()).getToken();
} catch (Exception exc) {
return null;
}
}
Then I override some of the configuration of the http in the configure method.
http
// Disable session management
.sessionManagement()
.sessionCreationPolicy(SessionCreationPolicy.STATELESS)
.and()
// Allow calls to OPTIONS
.authorizeRequests().antMatchers(HttpMethod.OPTIONS, "/**").permitAll()
.and()
// Authenticate every other call
.authorizeRequests().anyRequest().authenticated()
.and()
.csrf().disable();
Ideally, what I'd like to achieve is something like:
http.antMatchers("/foo").hasRole("jwt_extracted_role")
I am currently creating custom filters which extract roles from the token and then check for the correct roles, but this is maybe more cumbersome than it needs to be.
Any pointers on which methods of which configuration classes I should be looking to override to extract the roles from the request's, and add them to the request?
I ended up solving this by overriding the KeycloakAuthenticationProvider and providing my override class as a bean in the WebSecurityConfig. My class is below:
public class ResourceAwareAuthenticationProvider extends KeycloakAuthenticationProvider {
#Override
public Authentication authenticate(Authentication authentication) throws AuthenticationException {
... here I add granted authorities from the token's credentials ...
}
}
Then in my class WebSecurityConfigurer extends KeycloakWebSecurityConfigurerAdapter I override the AuthenticationProvider:
#Bean
#Override
public AuthenticationManager authenticationManagerBean() throws Exception {
return new ProviderManager(Lists.newArrayList(new ResourceAwareAuthenticationProvider()));
}
This allows me to do configuration like:
http.authorizeRequests()
.antMatchers("/**").hasAuthority("my-resource-authority")
The application logs all requested urls. This means, that it's critical not to authenticate using url parameters, because it would cause the situation in which logs are full of pairs (login=abc&password=123). For this reason I've configured spring-security to read parameters from request-body. It's done by adding the following line to the request-header:
'Content-Type': 'application/x-www-form-urlencoded'
The body will be:
{'login':'admin', 'password':'password'}
It's fine, but the QA forces me to disable the possibility of authentication via url paramters. At the moment a POST to the following URL will also authenticate:
https://example.com/foo?login=admin&password=password
Does anyone know a trick to disable this option? With an annotation preferably.
Due to the comment I decided to add some more details to my problem. My spring-security is configured with WebSecurityConfigurerAdapter. I have
http.usernameParameter("login")
.passwordParameter("password")
(...)
This makes Spring searching login data in both - parameters and body. I wish to disable searching those parameters in the url.
This makes Spring searching login data in both - parameters and body. I wish to disable searching those parameters in the url.
I believe this is not possible since this behaviour is not implemented by Spring rather than JavaEE itself.
HttpServletRequest.getParameter doc states:
Returns the value of a request parameter as a String, or null if the parameter does not exist. Request parameters are extra information sent with the request. For HTTP servlets, parameters are contained in the query string or posted form data.
But you can try to alter this with filter that should look something like this:
public class DisableGetAuthFiler extends OncePerRequestFilter {
...
#Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
filterChain.doFilter(
new HttpServletRequestWrapper(request) {
#Override
public String getParameter(String name) {
if (("login".equals(name) && getQueryString().contains("login"))
|| ("password".equals(name) && getQueryString().contains("password"))) {
return null;
} else {
return super.getParameter(name);
}
}
},
response
);
}
}
EDIT Haim Raman proposed another solution that uses existing filter instead of introducing a new one. Only I would suggest overriding obtainUsername() and obtainPassword() instead of attemptAuthentication().
I would like to suggest an alternative which is based on spring-security rater then a workaround as suggested by chimmi.
This answer provide a solution to the issue suggested by xenteros on bres26 answer as well
Override the exiting UsernamePasswordAuthenticationFilter implementation
public class ImprovedUsernamePasswordAuthenticationFilter
extends UsernamePasswordAuthenticationFilter {
#Override
protected String obtainUsername(HttpServletRequest request) {
final String usernameParameter = getUsernameParameter();
validateQueryParameter(request, usernameParameter);
return super.obtainUsername(request);
}
#Override
protected String obtainPassword(HttpServletRequest request) {
final String passwordParameter = getPasswordParameter();
validateQueryParameter(request, passwordParameter);
return super.obtainPassword(request);
}
private void validateQueryParameter(HttpServletRequest request, String parameter) {
final String queryString = request.getQueryString();
if (!StringUtils.isEmpty(queryString)) {
if (queryString.contains(parameter))
throw new AuthenticationServiceException("Query parameters for login are a prohibit, use message body only!");
}
}
}
You need to replace your own implementation with the existing one (see doc here)
#Configuration
#EnableWebSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.authorizeRequests()
.antMatchers("/", "/home","/login").permitAll()
.anyRequest().authenticated()
.and()
.logout()
.permitAll()
.and()
//Replace FORM_LOGIN_FILTER with your own custom implementation
.addFilterAt(improvedUsernamePasswordAuthenticationFilter(), UsernamePasswordAuthenticationFilter.class)
.exceptionHandling()
.authenticationEntryPoint(new LoginUrlAuthenticationEntryPoint("/login"))
.and()
//disable csrf to allow easy testing
.csrf().disable();
}
#Autowired
public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
auth
.inMemoryAuthentication()
.withUser("user").password("password").roles("USER");
}
public UsernamePasswordAuthenticationFilter improvedUsernamePasswordAuthenticationFilter() throws Exception {
UsernamePasswordAuthenticationFilter authFilter = new ImprovedUsernamePasswordAuthenticationFilter();
authFilter.setRequiresAuthenticationRequestMatcher(
new AntPathRequestMatcher("/login", "POST")
);
authFilter
.setAuthenticationManager(authenticationManager());
authFilter
.setAuthenticationSuccessHandler(
new SavedRequestAwareAuthenticationSuccessHandler()
);
authFilter
.setAuthenticationFailureHandler(
new SimpleUrlAuthenticationFailureHandler("/login?error")
);
return authFilter;
}
}
Advantages: it’s based on spring security and flexible to changes.
Disadvantage: Unfortunately I found Spring Java Config very hard to set and to read
EDIT: I accepted chimmi comment and overridden obtainUsername and obtainPassword
You can find the source code in github.
To the best of my knowledge and intuition, like jhan had mentioned, the appropriate solution would be to use annotation #RequestMapping(value="/login", method="RequestMethod.POST"). Then, no matter what parameters the user may pass with the URL, both the URL and URI will always default to /login. And that is what the logger will document. Not the username and password pairs, but "http://localhost:8080/login", or whatever your port is.
You can achieve this by modifying the UsernamePasswordAuthenticationFilter's RequestMatcher. For example:
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.formLogin()
.withObjectPostProcessor(new ObjectPostProcessor<UsernamePasswordAuthenticationFilter>() {
#Override
public <O extends UsernamePasswordAuthenticationFilter> O postProcess(
O filter) {
AntPathRequestMatcher pathMatcher = new AntPathRequestMatcher("/login", "POST");
RequestMatcher noQuery = new RequestMatcher() {
#Override
public boolean matches(HttpServletRequest request) {
return request.getQueryString() == null;
}
};
AndRequestMatcher matcher = new AndRequestMatcher(Arrays.asList(pathMatcher, noQuery));
filter.setRequiresAuthenticationRequestMatcher(matcher);
return filter;
}
})
.and()
...
}
}
NOTE: The requirement below does not prevent a GET request from being issued (and thus leaking the credentials). It is really up to the UI to ensure this doesn't happen.
It's fine, but the QA forces me to disable the possibility of
authentication via url paramters.
I know, there are many articles about this topic, but I have a problem and I can't find any solution.
I have a classic spring security java config:
#Configuration
#EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
#Autowired
private AuctionAuthenticationProvider auctionAuthenticationProvider;
#Autowired
public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
auth.authenticationProvider(auctionAuthenticationProvider);
}
#Override
protected void configure(HttpSecurity http) throws Exception {
http.httpBasic();
ExpressionUrlAuthorizationConfigurer<HttpSecurity>.ExpressionInterceptUrlRegistry authorizeRequest = http.authorizeRequests();
configureAdminPanelAccess(authorizeRequest);
configureFrontApplicationAccess(authorizeRequest);
configureCommonAccess(authorizeRequest);
http.csrf()
.csrfTokenRepository(csrfTokenRepository()).and()
.addFilterAfter(csrfHeaderFilter(), CsrfFilter.class);
http.logout()
.clearAuthentication(true)
.invalidateHttpSession(true);
}
...
}
Also, I have two controller methods, where I login/logout from my web application by AJAX.
When I would like to logout, I first call this method, which I expect to clear user sessions and clear everything from the security context.
#Override
#RequestMapping(value = "/logout", method = GET, produces = APPLICATION_JSON_UTF8_VALUE)
public ResponseEntity<Boolean> logout(final HttpServletRequest request, final HttpServletResponse response) {
Authentication auth = SecurityContextHolder.getContext().getAuthentication();
if (auth != null){
new SecurityContextLogoutHandler().logout(request, response, auth);
}
return new ResponseEntity<>(Boolean.TRUE, HttpStatus.OK);
}
After this I reload my client web application and each time, when it is reloaded, I check whether the user is authenticated by calling the following controller method:
#Override
#RequestMapping(value = "/user", method = GET, produces = APPLICATION_JSON_UTF8_VALUE)
public ResponseEntity<UserDetails> user() {
Object principal = SecurityContextHolder.getContext().getAuthentication().getPrincipal();
if (principal instanceof UserDetails) {
return new ResponseEntity<>((UserDetails) principal, HttpStatus.OK);
}
return null;
}
And here I aways receive the last authenticated user. It seems that in the previous logout method, Spring logout doesn't work.
Keep in mind that I tried to logout with the following code, without any success:
#Override
#RequestMapping(value = "/logout", method = GET, produces = APPLICATION_JSON_UTF8_VALUE)
public ResponseEntity<Boolean> logout(final HttpServletRequest request) {
try {
request.logout();
return new ResponseEntity<>(Boolean.TRUE, HttpStatus.OK);
} catch (ServletException ex) {
if (LOG.isDebugEnabled()) {
LOG.debug("There is a problem with the logout of the user", ex);
}
}
Are you have any idea what I miss in my config and the logout process?
From your question, I see you are trying to create your own logout and you also trying to use the default Spring logout. I advise that you should choose one method and not mix them both. There are two I recommend to logout from Spring:
First: Default spring security logout
.logout().logoutRequestMatcher(new AntPathRequestMatcher("/logout"))
.logoutSuccessUrl("/logout.done").deleteCookies("JSESSIONID")
.invalidateHttpSession(true)
From the example above, you should only need to call the /logout URL whenever you want to logout the user. No need to create any #Controller to handle that logout instead Spring will help to log the user out. You also can add other thing you want to invalidate here.
Second: Programmatically logout
#RequestMapping(value = {"/logout"}, method = RequestMethod.POST)
public String logoutDo(HttpServletRequest request,HttpServletResponse response){
HttpSession session= request.getSession(false);
SecurityContextHolder.clearContext();
session= request.getSession(false);
if(session != null) {
session.invalidate();
}
for(Cookie cookie : request.getCookies()) {
cookie.setMaxAge(0);
}
return "logout";
}
If you are using this logout approach, you don't need to include the first method in ht eSpring security config. By using this method, you can add an extra action to perform before and after logout done. BTW, to use this logout, just call the /logout url and the user will be logged out manually. This method will invalidate the session, clear Spring security context and cookies.
In addition for the second method, if you are using RequestMethod.POST, you need to include the CSRF key on the POST request. The alternative way is to create a form with a hidden input CSRF key. This is some example of auto generated logout link with jQuery :
$("#Logout").click(function(){
$form=$("<form>").attr({"action":"${pageContext.request.contextPath}"+"/logout","method":"post"})
.append($("<input>").attr({"type":"hidden","name":"${_csrf.parameterName}","value":"${_csrf.token}"}))
$("#Logout").append($form);
$form.submit();
});
You just need to create a hyperlink <a id="Logout">Logout</a> to use it.
If you are using RequestMethod.GET,just include a CSRF key as a parameter in you link like this:
Logout
Thats all, hope it helps.
Just a heads up, there is Clear Site Data HTTP header as shown below
Clear-Site-Data: "cache", "cookies", "storage", "executionContexts"
I also helped add support for Clear-Site-Data header into Spring-Security 5.2 project. For more details around the implementation, see the PR.
Here is a sample of how it is going to work
#EnableWebSecurity
static class HttpLogoutConfig extends WebSecurityConfigurerAdapter {
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.logout()
.addLogoutHandler(new HeaderWriterLogoutHandler(
new ClearSiteDataHeaderWriter(SOURCE)));
}
}
Where SOURCE is a vararg of one or more of the following
"*" Clear everything
One or more of "cache", "cookies", "storage", "executionContexts"
For more details see the sample test in the LogoutConfigurerClearSiteDataTests.java.
This will help, i think clearAuthentication(true) is enough:
#Configuration
#EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
....
#Override
protected void configure(HttpSecurity http) throws Exception
{
http
.httpBasic()
.and()
.logout().clearAuthentication(true)
.logoutSuccessUrl("/")
.deleteCookies("JSESSIONID")
.invalidateHttpSession(true)
.and()
I solved my problem similarly by adding the following parameter to the application.properties file
spring.cache.type=NONE
Just change logout URL from "/logout" to "war or snapshot name/logout"