Spring 4 Security Custom LogoutSuccessHandler Example
March 27, 2016
On this page we will provide spring 4 security custom LogoutSuccessHandler example. The responsibility of LogoutSuccessHandler is to redirect or forward the page to desired location after successful logout. Once we configure LogoutSuccessHandler using http.logoutSuccessHandler() in our security configuration, it takes precedence over http.logoutSuccessUrl() if configured. Custom LogoutSuccessHandler is useful where we need to perform some task after successful logout. It is also useful to forward the page only with HTTP status code in header useful in REST web service.
LogoutSuccessHandler
LogoutSuccessHandler
is called after successful logout in our spring security application. The responsibility of this class is to redirect or forward to required destination with appropriate HttpStatus
. To use LogoutSuccessHandler
implementation classes, we need to pass its instance to http.logoutSuccessHandler()
method.
http.logout(). logoutUrl("/appLogout"). http.logoutSuccessHandler(myLogoutSuccessHandler); //.logoutSuccessUrl("/app");
http.logoutSuccessHandler()
, it takes precedence over http.logoutSuccessUrl()
configuration. LogoutSuccessHandler
has two implementations.
SimpleUrlLogoutSuccessHandler : It handles the navigation on successful logout. We use it as follows.
http.logout(). logoutUrl("/appLogout"). logoutSuccessHandler(new SimpleUrlLogoutSuccessHandler());
HttpStatusReturningLogoutSuccessHandler : It has been introduced in spring web security 4. It sends only HTTP status code in response header. It is useful where there is no redirection required after successful logout such as REST web service. We can set required HTTP status code passing as constructor argument, by default it sends
HttpStatus.OK
.
http.logout(). logoutUrl("/appLogout"). logoutSuccessHandler(new HttpStatusReturningLogoutSuccessHandler(HttpStatus.ACCEPTED));
Custom LogoutSuccessHandler
For customLogoutSuccessHandler
, we need to create a class implementing LogoutSuccessHandler
interface and override onLogoutSuccess()
method that can throw exception unlike LogoutHandler.logout()
. Using custom LogoutSuccessHandler
, we can perform any task which is needed after successful logout. Here we can redirect the page to required destination with required HTTP status code.
MyLogoutSuccessHandlerOne.java
package com.concretepage.handler; import java.io.IOException; import javax.servlet.ServletException; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import org.springframework.http.HttpStatus; import org.springframework.security.core.Authentication; import org.springframework.security.web.authentication.logout.LogoutSuccessHandler; import org.springframework.stereotype.Component; @Component public class MyLogoutSuccessHandlerOne implements LogoutSuccessHandler { @Override public void onLogoutSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication) throws IOException, ServletException { if(authentication != null) { System.out.println(authentication.getName()); } //perform other required operation String URL = request.getContextPath() + "/app"; response.setStatus(HttpStatus.OK.value()); response.sendRedirect(URL); } }
MyLogoutSuccessHandlerTwo.java
package com.concretepage.handler; import java.io.IOException; import javax.servlet.ServletException; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import org.springframework.http.HttpStatus; import org.springframework.security.core.Authentication; import org.springframework.security.web.authentication.logout.LogoutSuccessHandler; import org.springframework.stereotype.Component; @Component public class MyLogoutSuccessHandlerTwo implements LogoutSuccessHandler { @Override public void onLogoutSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication) throws IOException, ServletException { if(authentication != null) { System.out.println(authentication.getName()); } //perform other required operation response.setStatus(HttpStatus.OK.value()); response.getWriter().flush(); } }
SecurityConfig.java
package com.concretepage.config; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.annotation.Configuration; 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; import com.concretepage.handler.MyLogoutSuccessHandlerOne; @Configuration @EnableWebSecurity public class SecurityConfig extends WebSecurityConfigurerAdapter { @Autowired private MyLogoutSuccessHandlerOne myLogoutSuccessHandler; @Override protected void configure(HttpSecurity http) throws Exception { //authorize requests http.authorizeRequests(). antMatchers("/app/secure/**"). access("hasRole('ROLE_ADMIN')"); //login configuration http.formLogin(). loginPage("/app"). loginProcessingUrl("/appLogin"). usernameParameter("app_username"). passwordParameter("app_password"). defaultSuccessUrl("/app/secure/home"); //logout configuration http.logout(). logoutUrl("/appLogout"). logoutSuccessHandler(myLogoutSuccessHandler); } @Autowired public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception { auth.inMemoryAuthentication().withUser("ram").password("ram123").roles("ADMIN"); } }
Software Used
Find the software used in our demo.1. Java 8
2. Spring-Security 4.0.3.RELEASE
3. Tomcat 8
4. Gradle
5. Eclipse
Gradle File
build.gradleapply plugin: 'java' apply plugin: 'eclipse' apply plugin: 'war' archivesBaseName = 'spring4javaconfig' version = '1' repositories { mavenCentral() } dependencies { compile 'org.springframework.boot:spring-boot-starter-web:1.3.3.RELEASE' compile 'org.springframework.boot:spring-boot-starter-security:1.3.3.RELEASE' compile 'jstl:jstl:1.2' providedRuntime 'org.springframework.boot:spring-boot-starter-tomcat:1.3.3.RELEASE' }
Other Java Files and Views used in Example
AppConfig.javapackage com.concretepage.config; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.ComponentScan; import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Import; import org.springframework.web.servlet.config.annotation.EnableWebMvc; import org.springframework.web.servlet.view.InternalResourceViewResolver; @Configuration @ComponentScan("com.concretepage") @Import(SecurityConfig.class) @EnableWebMvc public class AppConfig { @Bean public InternalResourceViewResolver viewResolver() { InternalResourceViewResolver resolver = new InternalResourceViewResolver(); resolver.setPrefix("/WEB-INF/secure/"); resolver.setSuffix(".jsp"); return resolver; } }
package com.concretepage.config; import org.springframework.security.web.context.AbstractSecurityWebApplicationInitializer; public class SecurityInitializer extends AbstractSecurityWebApplicationInitializer { }
package com.concretepage.config; import org.springframework.web.servlet.support.AbstractAnnotationConfigDispatcherServletInitializer; public class WebAppInitializer extends AbstractAnnotationConfigDispatcherServletInitializer { @Override protected Class<?>[] getRootConfigClasses() { return new Class[] { AppConfig.class }; } @Override protected Class<?>[] getServletConfigClasses() { return null; } @Override protected String[] getServletMappings() { return new String[] { "/" }; } }
package com.concretepage.controller; import java.util.ArrayList; import java.util.List; import org.springframework.stereotype.Controller; import org.springframework.ui.Model; import org.springframework.web.bind.annotation.RequestMapping; import com.concretepage.bean.Student; @Controller @RequestMapping("/app") public class StudentController { @RequestMapping public String login() { return "customLogin"; } @RequestMapping("/secure/home") public String homePage(Model model) { List<Student> list = new ArrayList<>(); list.add(new Student(1, "Shankar")); list.add(new Student(2, "Vishnu")); list.add(new Student(3, "Bhahma")); model.addAttribute("list", list); return "home"; } }
package com.concretepage.bean; public class Student { private int stdId; private String stdName; public Student(int stdId, String stdName) { this.stdId = stdId; this.stdName = stdName; } public int getStdId() { return stdId; } public void setStdId(int stdId) { this.stdId = stdId; } public String getStdName() { return stdName; } public void setStdName(String stdName) { this.stdName = stdName; } }
<%@taglib uri="http://www.springframework.org/tags/form" prefix="form"%> <html> <head> <title>Spring Security Example</title> </head> <body> <h3>Spring Security Example</h3> <font color="red"> ${SPRING_SECURITY_LAST_EXCEPTION.message} </font> <form:form action="${pageContext.request.contextPath}/appLogin" method="POST"> Enter UserName: <input type="text" name="app_username"/><br/><br/> Enter Password: <input type="password" name="app_password"/> <br/><br/> <input type="submit" value="Login"/> </form:form> <body> </html>
<%@taglib uri="http://www.springframework.org/tags/form" prefix="form"%> <%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %> <html> <head> <title>Spring Security Example</title> </head> <body> <form:form action="${pageContext.request.contextPath}/appLogout" method="POST"> <input type="submit" value="Logout"/> </form:form> Welcome to you. Find secure data. <br/> <c:forEach var="ob" items="${list}"> <br/><c:out value="${ob.stdId}"></c:out> <c:out value="${ob.stdName}"></c:out> </c:forEach> </body> </html>