The Master Class of "Learn Spring Security" is out:

>> CHECK OUT THE COURSE

1. Overview

A common requirement for a web application is to redirect different types of users to different pages after login. An example of this would be redirecting standard users to a /homepage.html page and admin users to a /console.html page for example.

This article will show how to quickly and safely implement this mechanism using Spring Security. The article is also building on top of the Spring MVC tutorial which deals with setting up the core MVC stuff necessary for the project.

2. The Spring Security Configuration

Spring Security provides a component that has the direct responsibility of deciding what to do after a successful authentication – the AuthenticationSuccessHandler. This strategy component is configurable via the namespace:

<?xml version="1.0" encoding="UTF-8"?>
<beans:beans 
    xmlns="http://www.springframework.org/schema/security"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
    xmlns:beans="http://www.springframework.org/schema/beans"
    xsi:schemaLocation="
        http://www.springframework.org/schema/security 
        http://www.springframework.org/schema/security/spring-security-3.1.xsd
        http://www.springframework.org/schema/beans 
        http://www.springframework.org/schema/beans/spring-beans-3.2.xsd">

    <http use-expressions="true" >
        <intercept-url pattern="/login*" access="permitAll" />
        <intercept-url pattern="/**" access="isAuthenticated()" />

        <form-login login-page='/login.html' 
            authentication-failure-url="/login.html?error=true"
            authentication-success-handler-ref="myAuthenticationSuccessHandler"/>

        <logout/>
    </http>

    <beans:bean id="myAuthenticationSuccessHandler"
        class="org.baeldung.security.MySimpleUrlAuthenticationSuccessHandler" />

    <authentication-manager>
        <authentication-provider>
            <user-service>
                <user name="user1" password="user1Pass" authorities="ROLE_USER" />
                <user name="admin1" password="admin1Pass" authorities="ROLE_ADMIN" />
            </user-service>
        </authentication-provider>
    </authentication-manager>

</beans:beans>

The parts of this configuration to focus on are the definition of the custom authentication success handler bean and using that bean to configure the <form-login> element and the authentication-success-handler-ref attribute.

The rest of the configuration is pretty standard stuff: a single, simple <http> element securing everything and only permitting unauthenticated access to /login*, and the standard in-memory authentication provider to keep things simple.

3. The Custom Authentication Success Handler

Besides the AuthenticationSuccessHandler interface, Spring also provides a sensible default for this strategy component – the AbstractAuthenticationTargetUrlRequestHandler and a simple implementation – the SimpleUrlAuthenticationSuccessHandler. Typically these implementations will determine the URL after login and perform a redirect to that URL.

While somewhat flexible, the mechanism to determine this target URL does not allow the determination to be done programmatically – so we’re going to implement the interface and provide a custom implementation of the success handler. This implementation is going to determine the URL to redirect the user to after login based on the role of the user:

public class MySimpleUrlAuthenticationSuccessHandler implements AuthenticationSuccessHandler {
    protected Log logger = LogFactory.getLog(this.getClass());

    private RedirectStrategy redirectStrategy = new DefaultRedirectStrategy();

    @Override
    public void onAuthenticationSuccess(HttpServletRequest request, 
      HttpServletResponse response, Authentication authentication) throws IOException {
        handle(request, response, authentication);
        clearAuthenticationAttributes(request);
    }

    protected void handle(HttpServletRequest request, 
      HttpServletResponse response, Authentication authentication) throws IOException {
        String targetUrl = determineTargetUrl(authentication);

        if (response.isCommitted()) {
            logger.debug("Response has already been committed. Unable to redirect to " + targetUrl);
            return;
        }

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

    /** Builds the target URL according to the logic defined in the main class Javadoc. */
    protected String determineTargetUrl(Authentication authentication) {
        boolean isUser = false;
        boolean isAdmin = false;
        Collection<? extends GrantedAuthority> authorities = authentication.getAuthorities();
        for (GrantedAuthority grantedAuthority : authorities) {
            if (grantedAuthority.getAuthority().equals("ROLE_USER")) {
                isUser = true;
                break;
            } else if (grantedAuthority.getAuthority().equals("ROLE_ADMIN")) {
                isAdmin = true;
                break;
            }
        }

        if (isUser) {
            return "/homepage.html";
        } else if (isAdmin) {
            return "/console.html";
        } else {
            throw new IllegalStateException();
        }
    }

    protected void clearAuthenticationAttributes(HttpServletRequest request) {
        HttpSession session = request.getSession(false);
        if (session == null) {
            return;
        }
        session.removeAttribute(WebAttributes.AUTHENTICATION_EXCEPTION);
    }

    public void setRedirectStrategy(RedirectStrategy redirectStrategy) {
        this.redirectStrategy = redirectStrategy;
    }
    protected RedirectStrategy getRedirectStrategy() {
        return redirectStrategy;
    }
}

The determineTargetUrl – which is the core of the strategy – simply looks at the type of user (determined by the authority) and picks the target URL based on this role.

So, an admin user – determined by the ROLE_ADMIN authority – will be redirected to the console page after login, while the standard user – as determined by ROLE_USER – will be redirected to the homepage.

4. Conclusion

The implementation of this Spring Redirect after Login Tutorial can be downloaded as a working sample project.

This is an Eclipse based project, so it should be easy to import and run as it is.

The Master Class "Learn Spring Security" is out:

>> CHECK OUT THE COURSE

  • Dummy Guy

    Thank you man! You helped me =)

  • Nice… Just what I was looking for… +1

  • jitendra kumawat

    Thanks, you did good job!!!

  • Wouter Samaey

    I tried to use an auto wired service and a request scoped bean here, but that doesn’t work. Any idea why I cannot access these? I get an error message saying that perhaps the code is executed outside of the request. Is there anything to this?

    • That will depend on weather or not you’re inside a request or not – do you have any sample project I could look at? You can fork the tutorial on github and include this new request bean so that I can easily replicate the exception. Cheers,
      Eugen.

  • Billy Turf

    Nice blog. What is the purpose of calling the method clearAuthenticationAttributes(request) ?

    • Hey Billy – so, the implementation of the AuthenticationSuccessHandler is mostly copied from the SimpleUrlAuthenticationSuccessHandler – with a few modifications, as explained above. That call comes from the original implementation with the purpose of removing any remaining session data after finishing the handling of the request. Cheers,
      Eugen.

  • Dipan Bhattacharyya

    Very well explained Eugen. Thank you. I was looking for a clean way to save the last login date for the user – and used your example to construct one that meets the needs.

    I didn’t have to use the redirectstrategy though – response.sendRedirect worked well for me.

  • Mr.Chowdary

    Thanks so much Eugen.. It really helped me a lot.. I googled and no use at all at my favourite site stackoverflow.com.. Thanks so much again.. Keep post very useful posts like this..

  • Ashwani kumar

    Awesome post….
    It’s like having fun to learn from your site. Cheers.

  • John Gunvaldson

    In the (pretty common) scenario where a user has both ROLE_USER “and” ROLE_ADMIN, then the loop that cruises through all authorities may exit early (break out) before encountering that the user is also an ADMIN. It work work if a flag was set that some Boolean logic could check – i.e. both, then ADMIN, only user then USER. Something like that.

  • velmurugan

    Thank you so much Eugen !.. Great Post 🙂

  • Álvaro de Araújo

    Very good! Thanks so much.

  • Ana M

    Thanks for this post. I am trying to do the same thing but, using the onAuthenticationFailure event. Do you have an example that ilustrates this? What I really want to do is go back to the login page and show information messages according to authentication failure results whenever those occur.

    • Hey Ana – you don’t need to use that event for that – displaying the login page (along with some extra info) is pretty much standard practice. You can have a look at the registration series and at the github project of course – to get a working example of how to do that.
      Cheers,
      Eugen.

      • Ana M

        thank you, I ended up using the .exceptionHandling().accessDeniedPage() option in the http security object.

  • Guilherme Marquesini Reis Ribe

    Nice. thanks

  • Colin Plank

    I glanced over this article and decided it was too complicated to even attempt and quickly forgot about it. 8 hours and many other failed attempts later, I had implemented this almost identically. Just happened to come back to it since I had left the tab open. I need to learn to slow down and take wisdom from those who have gone before me.

    • Hey Colin – I can definitely relate, as it happened to me a few times over the years. All in all, it turned out to be a short term hit leading to being able to better evaluate things moving forward – so it was for the best.
      Cheers and keep in touch,
      Eugen.

  • Ganesh Kandasamy

    Hi, I need to redirect to a spring controller instead of .html.

    if (isUser) {

    return “/homepage.html”;//return to a controller

    } else if (isAdmin) {

    return “/console.html”;//return to a controller

    } else {

    throw new IllegalStateException();

    }

    • Well Ganesh – you can redirect to whatever your application handles. The new request you redirect to will be decided by the way your system handles that request.

  • Jmp Jmp

    excellent.

  • vijay garg

    When i am posting username and password to spring security module for authenticating it again redirect me to my custom login page without authenticating .My login page is out side the main application and hosted on local http apache server.Please help……

    • Hey Vijay,
      That’s an interesting question, but it’s unfortunately going to be difficult to answer without looking at the code. The way to go here would be a PR on Github with a failing test – and I’d be happy to have a look.
      Hope that helps. Cheers,
      Eugen.