Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Special Login with Spring Security

The company where i am currently working has a special kind of authentication process.

Indeed, several users can have the same login and password. So, to identify them, in a second time, the user has to give his email. But again, in a few cases, several users can be concerned and then, the user has to give his company id to be full authenticated.

So, my problem is that in some cases, the authentication process cannot be done in one step, which is the default behaviour of most applications that Spring Security handles out of the box.

So my question is : what is the simpliest way to implement with Spring Security this special login process ?

Thanks in advance.

like image 655
rico Avatar asked Jul 21 '11 19:07

rico


1 Answers

I had to do a similar two-step authentication process. It sounds simple, but finding the right places to inject and the right methods to override was not easy. The basic idea is to provide another role for the intermediate authentication (the email check), then grant full access after the email is confirmed.

Hopefully the code below provides some good hints on how it could be solved for your scenario.

Create a custom UserDetailsChecker to handle post-authentication checks. This is where the user's role is determined.

public class CustomPostAuthenticationChecks implements UserDetailsChecker {

        public void check(UserDetails userDetails) {

            CustomUser customUser = (CustomUser) userDetails;
            if (customUser.needsEmailAuthentication()) {
                // Get rid of any authorities the user currently has
                userDetails.getAuthorities().clear();
                // Set the new authority, only allowing access to the 
                // email authentication page.
                userDetails.getAuthorities().add(new GrantedAuthorityImpl("ROLE_NEEDS_EMAIL_AUTH"));
            } else {
                userDetails.getAuthorities().add(new GrantedAuthorityImpl("ROLE_AUTHORIZED_USER"));
            }
    }

Create a custom AuthenticationSuccessHandler. This class sends the user to the correct URL based on their role.

public class CustomAuthenticationSuccessHandler extends SavedRequestAwareAuthenticationSuccessHandler {

@Override
protected String determineTargetUrl(HttpServletRequest request, HttpServletResponse response) {

    String targetUrl = null;

    SecurityContext securityContext = SecurityContextHolder.getContext();

    Collection<GrantedAuthority> authorities = securityContext.getAuthentication().getAuthorities();

    if (authorities.contains(new GrantedAuthorityImpl("ROLE_NEEDS_EMAIL_AUTH"))) {
        targetUrl = "/authenticate";
    } else if (authorities.contains(new GrantedAuthorityImpl("ROLE_AUTHORIZED_USER"))) {
        targetUrl = "/authorized_user_url";
    } else {
        targetUrl = super.determineTargetUrl(request, response);
    }

    return targetUrl;
}

After the user's email address is authenticated, you will need to grant the user full access to the application:

public void grantUserAccess(User user) {
    SecurityContext securityContext = SecurityContextHolder.getContext();
    Authentication auth = securityContext.getAuthentication();
    user.getAuthorities().clear();
    user.getAuthorities().add(new GrantedAuthorityImpl("ROLE_AUTHORIZED_USER"));
    Authentication newAuth = new UsernamePasswordAuthenticationToken(user, auth.getCredentials(), user.getAuthorities());
    securityContext.setAuthentication(newAuth);
}

Define a custom authentication provider to register your CustomPostAuthenticationChecks:

<security:authentication-manager>
    <security:authentication-provider ref="customAuthenticationProvider" />
</security:authentication-manager>

<bean id="customAuthenticationProvider" class="YourAuthenticationProvider">
    <property name="postAuthenticationChecks">
        <bean class="CustomPostAuthenticationChecks"/>
    </property>
</bean>

If you're using the standard form-login tag, you can easily define your custom AuthenticationSuccessHandler:

<security:form-login authentication-success-handler-ref="customAuthenticationSuccessHandler">

...

<bean id="customAuthenticationSuccessHandler" class="CustomAuthenticationSuccessHandler"/>

Add a new role intercept rule for the /authenticate URL, so only users who need more authentication can access that page.

<security:intercept-url pattern="/authenticate/**" access="hasRole('ROLE_NEEDS_EMAIL_AUTH')" />
<security:intercept-url pattern="/authorized_user_url/**" access="hasRole('ROLE_AUTHORIZED_USER')" />
like image 50
Brad Avatar answered Sep 29 '22 10:09

Brad