Skip to content

Custom Logout Filter handler in Spring 3

Spring Security provides comprehensive support for adding custom handlers for logout/login. The default logout simply logs the user out and goes to the login page. What if the application needs to determine the user’s role and redirect to a separate screen? For example, for an admin, a custom logout screen. What if the user is automatically logged out after 10 mins of user inactivity? You don’t want the user to see a login screen without any message on it. That’s bad.

Here’s an example demonstrating a custom logout handler using Spring 3 security module. The code has some incompatibility issues with Spring 2 as Spring 3 Security is not 100% backward compatibility.

Sample User

public class User extends org.springframework.security.core.userdetails.User {
 // Stuff
}

Custom Filter Handler

import java.io.IOException;

import javax.annotation.PostConstruct;
import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.springframework.security.core.Authentication;
import org.springframework.security.web.DefaultRedirectStrategy;
import org.springframework.security.web.RedirectStrategy;
import org.springframework.security.web.authentication.logout.LogoutFilter;
import org.springframework.security.web.authentication.logout.LogoutHandler;
import org.springframework.security.web.authentication.logout.LogoutSuccessHandler;
import org.springframework.security.web.authentication.logout.SecurityContextLogoutHandler;

import xxx.model.User;

public class LogoutFilterWrapper
	implements Filter {

	private String logoutSuccessfulUrl;

	private String logoutSuccessfulUrlAdmin;

	private String logoutSuccessfulInactivityUrl;

	private LogoutFilter filter;

	@PostConstruct
	protected void initialize() {

		final SecurityContextLogoutHandler context = new SecurityContextLogoutHandler();
		context.setInvalidateHttpSession( true );
		this.filter =
			new LogoutFilter( new CustomLogoutSuccessHandler(), new LogoutHandler[] { context } );
	}

	public void setLogoutSuccessfulUrl(
		String inUrl ) {

		this.logoutSuccessfulUrl = inUrl;
	}

	public void setLogoutSuccessfulUrlAdmin(
		String inUrl ) {

		this.logoutSuccessfulUrlAdmin = inUrl;
	}

	public void setLogoutSuccessfulUrlInactivity(
		String inUrl ) {

		this.logoutSuccessfulInactivityUrl = inUrl;
	}

	public final void init(
		FilterConfig inFilterConfig )
		throws ServletException {

		this.filter.init( inFilterConfig );
	}

	public final void destroy() {

		this.filter.destroy();
	}

	public final void doFilter(
		ServletRequest inRequest,
		ServletResponse inResponse,
		FilterChain inChain )
		throws IOException, ServletException {

		this.filter.doFilter( inRequest, inResponse, inChain );
	}

	/**
	 * Success Handler
	 */
	private class CustomLogoutSuccessHandler
		implements LogoutSuccessHandler {

		private RedirectStrategy redirectStrategy = new DefaultRedirectStrategy();

		@Override
		public void onLogoutSuccess(
			HttpServletRequest request,
			HttpServletResponse response,
			Authentication authentication )
			throws IOException, ServletException {

			String targetUrl = "";
			User principal = null;

			if ( authentication != null ) {
				if ( !(authentication.getPrincipal() instanceof User) ) {
					throw new IllegalArgumentException( "Invalid security principal type!" );
				}
				principal = (User) authentication.getPrincipal();
			}

			if ( principal == null ) {
				throw new IllegalStateException( "Security principal is not initialized!" );
			}

			final String loginUrl =
				principal.isAdministrator() ? LogoutFilterWrapper.this.logoutSuccessfulUrlAdmin
					: LogoutFilterWrapper.this.logoutSuccessfulUrl;

			final String timeoutUrl =
				LogoutFilterWrapper.this.logoutSuccessfulInactivityUrl
					+ "?login="
					+ request.getContextPath()
					+ loginUrl;

			targetUrl = request.getQueryString().contains( "timeout=true" ) ? timeoutUrl : loginUrl;

			redirectStrategy.sendRedirect( request, response, targetUrl );
		}
	}
}

Spring-config

<!-- Register a custom logout filter -->
<beans:bean id="customLogoutFilter" class="com.cramer.srf.security.LogoutFilterWrapper">
    <beans:property name="logoutSuccessfulUrl" value="/public/auth/login.htmlx" />
    <beans:property name="logoutSuccessfulUrlAdmin" value="/public/auth/admlogin.htmlx" />
    <beans:property name="logoutSuccessfulUrlInactivity" value="/public/auth/timedout.htmlx" />    
</beans:bean>
[Slashdot] [Digg] [Reddit] [del.icio.us] [Facebook] [Technorati] [Google] [StumbleUpon]

5 Comments

  1. Thadeu Knychala Tucc wrote:

    Thanks! That worked very well for me!

    Wednesday, March 16, 2011 at 8:53 am | Permalink
  2. Jobby wrote:

    Thanks for the post. I am new to spring and was looking into you code to put an audit for logout. I implemented this and when the application gets deployed the @PostConstruct method gets called.
    But when i logout using /xxxx/xxxx/j_spring_security_logout
    The ‘onLogoutSuccess’ method is never called.
    My config of this bean is

    Am i missing any further configuration (for example in web.xml)

    Any help will be appreciated.

    Thanks
    Jobby

    Friday, April 15, 2011 at 2:22 am | Permalink
  3. Jobby wrote:

    Sorry, the bean configuration did not show up
    <bean id=”customLogoutFilter” class=”com.xxxx.web.filter.LogoutFilterWrapper” >
    <\bean>

    Thanks again
    Jobby

    Friday, April 15, 2011 at 2:26 am | Permalink
  4. avinash wrote:

    hi,
    i have a Tag file “head.tag” in that i want to implement the “logout” functionality in spring. . Now i want to call the default “logoutfilter” provided by spring. As i am new to spring i am not able to map that in my “Web.xml” and “spring-security-config.xml” .
    So please help me with Filter mapping as i want to redirect to my login page after success. please tell me the process.

    Saturday, December 17, 2011 at 2:55 am | Permalink
  5. avinash wrote:

    please tell me how to call the filter

    Saturday, December 17, 2011 at 2:56 am | Permalink

Post a Comment

Your email is never published nor shared. Required fields are marked *
*
*