Configure Spring Security to redirect to a different page after logging in

1. Overview

A common requirement for Web applications is to redirect different types of users to different pages after login. For example, redirect standard users to the /homepage.html page and redirect admin users to the /console.html page.

This article will show how to implement this mechanism quickly and securely using Spring Security. This article is also based on Spring MVC Tutorial Above, the tutorial touches on the core stuff needed to set up a project.

2. Spring Security Configuration

Spring Security provides a component that is directly responsible for deciding what to do after a successful authentication - the AuthenticationSuccessHandler.

2.1. basic configuration

Let's first configure a basic @Configuration and @Service class:

@Configuration
@EnableWebSecurity
public class SecSecurityConfig extends WebSecurityConfigurerAdapter {

    @Override
    protected void configure(final HttpSecurity http) throws Exception {
        http
            .authorizeRequests()
            // ... endpoints
            .formLogin()
                .loginPage("/login.html")
                .loginProcessingUrl("/login")
                .defaultSuccessUrl("/homepage.html", true)
            // ... other configuration       
    }
}

The part of this configuration to focus on is the defaultSuccessUrl() method. After a successful login, any user will be redirected to homepage.html.

Also, we need to configure users and their roles. For the purpose of this article, we will implement a simple UserDetailService with two users, each with a role. For more information on this topic, read our article Spring Security -- Roles and Privileges.

@Service
public class MyUserDetailsService implements UserDetailsService {

    private Map<String, User> roles = new HashMap<>();

    @PostConstruct
    public void init() {
        roles.put("admin2", new User("admin", "{noop}admin1", getAuthority("ROLE_ADMIN")));
        roles.put("user2", new User("user", "{noop}user1", getAuthority("ROLE_USER")));
    }

    @Override
    public UserDetails loadUserByUsername(String username) {
        return roles.get(username);
    }

    private List<GrantedAuthority> getAuthority(String role) {
        return Collections.singletonList(new SimpleGrantedAuthority(role));
    }
}

Also note that in this simple example, we won't be using a password encoder.

2.2. Add custom success handler

We now have two users with two different roles: user and admin . After a successful login, both will be redirected to hompeage.html . Let's see how to redirect differently depending on the user's role.

First, we need to define our custom success handler as a bean:

@Bean
public AuthenticationSuccessHandler myAuthenticationSuccessHandler(){
    return new MySimpleUrlAuthenticationSuccessHandler();
}
@Override
protected void configure(final HttpSecurity http) throws Exception {
    http
        .authorizeRequests()
        // endpoints
        .formLogin()
            .loginPage("/login.html")
            .loginProcessingUrl("/login")
            .successHandler(myAuthenticationSuccessHandler())
        // other configuration      
}

2.3. XML configuration

Before looking at the implementation of our custom success handler, let's also look at the equivalent XML configuration:

<http use-expressions="true" >
    <!-- other configuration -->
    <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="com.baeldung.security.MySimpleUrlAuthenticationSuccessHandler" />

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

3. Custom authentication success handler

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

While somewhat flexible, the mechanism for determining this target URL does not allow for programmatic determination - so we will implement the interface and provide a custom implementation of the success handler. This implementation will determine the URL to which the user is redirected after logging in, based on the user's role.

First, we need to override the onAuthenticationSuccess method:

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);
    }

Our custom method calls two helper methods:

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);
}

The following method does the actual work and maps the user to the target URL:

protected String determineTargetUrl(final Authentication authentication) {

    Map<String, String> roleTargetUrlMap = new HashMap<>();
    roleTargetUrlMap.put("ROLE_USER", "/homepage.html");
    roleTargetUrlMap.put("ROLE_ADMIN", "/console.html");

    final Collection<? extends GrantedAuthority> authorities = authentication.getAuthorities();
    for (final GrantedAuthority grantedAuthority : authorities) {
        String authorityName = grantedAuthority.getAuthority();
        if(roleTargetUrlMap.containsKey(authorityName)) {
            return roleTargetUrlMap.get(authorityName);
        }
    }

    throw new IllegalStateException();
}

Note that this method will return the mapping URL for the first role the user has. Therefore, if the user has multiple roles, the mapped URL will match the first role given in the authorities collection.

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

determineTargetUrl - this is the core of the policy - just look at the user type (determined by permissions) and choose the target URL based on this role.

So the admin user - determined by the ROLE_ADMIN permission - will be redirected to the console page after login, while the standard user - determined by the ROLE_USER - will be redirected to the home page.

4. in conclusion

Code mentioned in this article [on GitHub] ( https://github.com/eugenp/tut... ) available. This is a Maven based project so it should be easy to import and run.

Tags: Back-end

Posted by netbros on Mon, 10 Oct 2022 18:01:35 +0300