Spring Security : Create a Custom Authentication Filter

Points To Remember

You may need to create an AuthenticatioFilter when you want to create a custom logic for handling the authentication filter. You may also want to create your own Authentication Provider, Entry Point, Authentication Token etc to customize the authentication process to a new level.

Step 1 : Create a Filter

Let us first create a class named MyAuthenticationFilter and then register it as a bean in resources.groovy. After we have created the class and registered the it as a bean, we can use this class as a filter for our custom spring security authentication.

Class : MyAuthenticationFilter.groovy
package com.ekiras

import org.springframework.context.ApplicationEventPublisher
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken
import org.springframework.security.authentication.event.InteractiveAuthenticationSuccessEvent
import org.springframework.security.core.Authentication
import org.springframework.security.core.AuthenticationException
import org.springframework.security.core.context.SecurityContextHolder
import org.springframework.security.web.authentication.AbstractAuthenticationProcessingFilter
import org.springframework.security.web.authentication.AuthenticationFailureHandler
import org.springframework.security.web.authentication.AuthenticationSuccessHandler

import javax.servlet.FilterChain
import javax.servlet.ServletException
import javax.servlet.ServletRequest
import javax.servlet.ServletResponse
import javax.servlet.http.HttpServletRequest
import javax.servlet.http.HttpServletResponse

/**
 * Created by ekansh on 19/1/15.
 */
class MyAuthenticationFilter extends AbstractAuthenticationProcessingFilter{

    public static final String USERNAME = 'j_username';
    public static final String PASSWORD = 'j_password';


    AuthenticationSuccessHandler authenticationSuccessHandler
    AuthenticationFailureHandler authenticationFailureHandler
    ApplicationEventPublisher applicationEventPublisher



    protected MyAuthenticationFilter() {
        super('/mylogin') // Register the url that will be intercepted by this filter.
    }

    @Override
    public void doFilter(ServletRequest req, ServletResponse resp, FilterChain chain)  throws IOException, ServletException {
        HttpServletRequest request = (HttpServletRequest) req
        HttpServletResponse response = (HttpServletResponse) resp
            //Check if the url contains the filterProcessUrl and required authentication is false, if not then pass the request to the next filter. 
        if (!request.getRequestURI().contains(filterProcessesUrl) && !requiresAuthentication(request, response)) {
            chain.doFilter(request, response)
            return
        }

        // Create an authentication token that will be returned.
        Authentication authentication;
        try{ // If the credentials to not match then an AuthenticationException is thrown.
            authentication = attemptAuthentication(request, response)
            // If successfully authenticated then pass the request to the success handler
            if(authentication.authenticated)
                successfulAuthentication(request,response,authentication)
        }
        catch(AuthenticationException exception){
            // Pass the request to authentication failure handler.
            unsuccessfulAuthentication(request,response,exception)
            return
        }
    }

    @Override
    protected boolean requiresAuthentication(HttpServletRequest request, HttpServletResponse response) {
        return true;
    }


    @Override
    Authentication attemptAuthentication(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse) throws AuthenticationException, IOException, ServletException {

        String username = getUsername(httpServletRequest);
        String password = getPassword(httpServletRequest);

        Authentication authentication = new UsernamePasswordAuthenticationToken(username, password);

        return this.authenticationManager.authenticate(authentication);
    }
    @Override
    protected void successfulAuthentication(HttpServletRequest request, HttpServletResponse response, Authentication authentication) throws IOException, ServletException{
        SecurityContextHolder.getContext().setAuthentication(authentication)
        applicationEventPublisher.publishEvent(new InteractiveAuthenticationSuccessEvent(authentication, this.class))
        authenticationSuccessHandler.onAuthenticationSuccess(request,response,authentication)
    }

    @Override
    protected void unsuccessfulAuthentication(HttpServletRequest request, HttpServletResponse response, AuthenticationException exception) throws IOException, ServletException{
        authenticationFailureHandler.onAuthenticationFailure(request,response, exception)
    }

    // Get the username from the request object
    String getUsername(HttpServletRequest request){
        return request.getParameter(USERNAME);
    }

    // get the password from the request object
    String getPassword(HttpServletRequest request){
        return request.getParameter(PASSWORD);
    }
}

Now we register the class as a spring bean in resources.groovy like this.
    myAuthenticationFilter(MyAuthenticationFilter){
        authenticationManager = ref('authenticationManager')
        sessionAuthenticationStrategy = ref('sessionAuthenticationStrategy')
        authenticationSuccessHandler = ref('authenticationSuccessHandler')
        authenticationFailureHandler = ref('authenticationFailureHandler')
        rememberMeServices = ref('rememberMeServices')
        authenticationDetailsSource = ref('authenticationDetailsSource')
        filterProcessesUrl = '/mylogin'
    }

Now you need to register this filter in the deployment descriptor so that the these effects take place. There are two ways of doing this

  1. You can declare your filter using a filter chain in Config.groovy. See the documentation how to do this.
  2.  You can declare the filter in bootsrap as following
    SpringSecurityUtils.clientRegisterFilter('myAuthenticationFilter', SecurityFilterPosition.SECURITY_CONTEXT_FILTER.order + 10)

No comments:

Powered by Blogger.