In web development, we usually cope with some problems about logging out a website. After logging out, we have to set the invalidation state of session, and delete our cookies …
But in Spring Boot, we do not have a specific solution for this problem.
So, in this article, we will discuss about logout problem in spring boot.
Table of contents
Solution
-
First solution, define in
configure()method directly.We can use the following code segment:
@Configuration @EnableWebSecurity public class WebSecurityConfig extends WebSecurityConfigurerAdapter implements WebMvcConfigurer { ... @Override protected void configure(HttpSecurity http) throws Exception { http.authorizeRequests() .antMatchers("/").access("hasRole('USER')") .antMatchers("/admin/**").access("hasRole('ADMIN')") .and() .formLogin() .loginProcessingUrl("/login") // link to submit username-password .loginPage("/login") .usernameParameter("username") // username field in login form .passwordParameter("password") // password field in login form .defaultSuccessUrl("/") .failureUrl("/login?error") .and() .logout() .logoutRequestMatcher(new AntPathRequestMatcher("/logout")) .logoutSuccessUrl("/login") .invalidateHttpSession(true) // set invalidation state when logout .deleteCookies("JSESSIONID") .and() .exceptionHandling() .accessDeniedPage("/403"); } ... }So, we will explain how Spring Security logout based on
AntPathRequestMatcherclass.While going to the URL
/logout, an object ofAntPathRequestMatcherclass will compare its link with link that is routed. If matched, Spring security will log out and implement sequence actions such asinvalidateHttpSession(),deleteCookies(), andlogoutSuccessUrl(). -
Second solution
We will not set up for logout information in
configure()method of WebSecurityConfig class.Then, we will process
/logoutin handling method ofController.@Configuration @EnableWebSecurity public class WebSecurityConfig extends WebSecurityConfigurerAdapter implements WebMvcConfigurer { ... @Override protected void configure(HttpSecurity http) throws Exception { http.authorizeRequests() .antMatchers("/api/**").permitAll() .antMatchers("/").access("hasRole('USER')") .antMatchers("/admin/**").access("hasRole('ADMIN')") .and() .formLogin() .loginProcessingUrl("/login") // link to submit username-password .loginPage("/login") .usernameParameter("username") .passwordParameter("password") .defaultSuccessUrl("/") .failureUrl("/login?error") .and() .exceptionHandling() .accessDeniedPage("/403"); } ... }@Controller public class LoginController { ... @GetMapping("/logout") public String fetchSignoutSite(HttpServletRequest request, HttpServletResponse response) { Authentication auth = SecurityContextHolder.getContext().getAuthentication(); if (auth != null) { new SecurityContextLogoutHandler().logout(request, response, auth); } return "redirect:/login?logout"; } ... }With this way, we will get information about user’s authentication by using
SecurityContextHolder.getContext().getAuthentication().If user was, then, we called
SecurityContextLogoutHandler().logout(request, response, auth)to logout user properly.The
logoutcall performs following:- Invalidates HTTP session, then unbinds any objects bound to it.
- Removes the Authentication from the SecurityContext to prevent issues with concurrent requests.
- Explicitly clears the context value from the current thread.
-
Third solution
In this solution, we will still use
configure()method, but there are some changes infetchSignoutSite()method inControllerclass.@RequestMapping(value = {"/logout"}, method = RequestMethod.GET) public String fetchSignoutSite(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 "redirect:/login?logout"; }If we are using this
fetchSignoutSite()method, we don’t need to include the first solution in Spring security config. By using this solution, we can add extra action to do before and after logout done. But, to use this solution, just call the/logouturl and user will be logout manually. This solution will invalidate session, clear spring security context and cookies.If we are using
RequestMethod.POST, we need to include thecsrfkey as a post. The alternative way is to create a form with hidden inputcsrfkey. 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(); });We just need to create hyperlink ```<a> id=”Logout”>Logout</a> to use it.
If we are using
RequestMethod.GET, just include acsrfkey as a parameter in our link like this:<a href="${pageContext.request.contextPath}/logout?${_csrf.parameterName}=${_csrf.token}">Logout</a>
Note: We have to go to a page with link /logout.
We can do that by defining anchor with the href = "@{/logout}" in thymeleaf:
<a th:href="@{/logout}">Logout</a>
The meanings of source code
-
Using
AntPathRequestMatcherclasspublic final class AntPathRequestMatcher extends java.lang.Object implements RequestMatcher, RequestVariablesExtractorMatcher will compares a pre-defined ant-style pattern against the URL (servletPath + pathInfo) of an
HttpServletRequest. The query string of the URL is ignored and matching is case-insensitive or case-sensitive depending on the arguments passed into the constructor.Using a pattern value of /** or ** is treated as a universal match, which will match any request. Patterns which end with /** (and have no other wildcards) are optimized by using a substring match — a pattern of /aaa/** will match /aaa, /aaa/ and any sub-directories, such as /aaa/bbb/ccc.
For all other cases, Spring’s
AntPathMatcheris used to perform the match.Note: Use
org.springframework.security.web.util.matcher.AntPathRequestMatcher, and not the deprecatedorg.springframework.security.web.util.AntPathRequestMatcherclass. -
Use
SecurityHolderContext,SecurityContextclassWe will have two declarations of
SecurityHolderContext,SecurityContextclass.public interface SecurityContext { // --> Obtains the currently authenticated principal, or an authentication request token. Authentication getAuthentication(); // --> changes the currently authenticated principal, or removes the authentication information. void setAuthentication(Authentication authentication); }public class SecurityContextHolder extends java.lang.Object { // Obtain the current SecurityContext. // returned value is never null public static SecurityContext getContext(); // Associates a new SecurityContext with the current thread of execution. // context may be not null public static void setContext(SecurityContext context); }The
interface SecurityContextwill define the minimum security information associated with the current thread of execution.And the security context is stored in a
SecurityContextHolderclass.SecurityContextis centering interface of Spring Security, saves all information that is relevant to the security in application. When we start Spring Security,SecurityContextwill be initialize.We can not access directly into
SecurityContext, but we can useSecurityContextHolderclass. This class will contain the currentSecurityContextof application, which includesprincipalthat is interacting with application.Spring Security will use
Authenticationobject to represent their information. The following code will help us getusernameandpasswordthat is typed from a user.Object principal = SecurityContextHolder.getContext().getAuthentication().getPrincipal(); if (principal instanceof UserDetails) { String username = ((UserDetails) principal).getUsername(); } else { String username = principal.toString(); }
Wrapping up
- In reality, we have two ways to logout in Spring security. It includes to set information in
configure()method and useSecurityContextHolderclass. - We can not access directly into
SecurityContextobject, we have to get it to depend onSecurityContextHolder.
Thanks for your reading.
Refer:
https://www.messycola.com/new-blog/2017/5/24/part-vi-integrating-spring-security-with-spring-boot
https://stackoverflow.com/questions/23661492/implement-logout-functionality-in-spring-boot
https://www.baeldung.com/spring-security-logout
https://developer.okta.com/blog/2017/12/04/basic-crud-angular-and-spring-boot
http://websystique.com/spring-security/spring-security-4-logout-example/