spring-securityspring-java-config

Spring Security with Java Configuration: How to handle BadCredentialsException from a custom provider


I need to authenticate some rest services using a token id in the url (or maybe in the request header - but this is not important for now). I am trying to use java configuration to set this up using as a guide this post. My problem is that I do not know how to handle "BadCredentialsException" that is thrown when the authentication fails from the provider. Here is my Security Config:

public static class SecurityConfigForRS extends
        WebSecurityConfigurerAdapter {

    @Autowired
    TokenAuthenticationProvider tokenAuthenticationProvider;

    @Override
    protected void configure(AuthenticationManagerBuilder auth)
            throws Exception {
        auth.authenticationProvider(tokenAuthenticationProvider);
    }

    @Bean
    @Override
    public AuthenticationManager authenticationManagerBean()
            throws Exception {
        return super.authenticationManagerBean();
    }

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        super.configure(http);

        http.regexMatcher("^/rest.*")
                .addFilterBefore(
                        new TokenAuthenticationFilter(
                                authenticationManagerBean()),
                        AbstractPreAuthenticatedProcessingFilter.class)
                .and().csrf().disable();

    }
}

For now I skip the other implementations - if it helps I will post them later.

When the token is missing or is invalid, the TokenAuthernticationProvider throws a BadCredentialsException. I need to catch this and send back an 401-Unauthorized. Is it possible to do this?


Solution

  • The first Filter I created was a subclass of GenericFilterBean and it did not have support for authentication failure handler or success handler. However AbstractAuthenticationProcessingFilter supports success and failure handlers. My filter is as simple as that:

    public class TokenAuthenticationProcessingFilter extends
        AbstractAuthenticationProcessingFilter {
    
    public TokenAuthenticationProcessingFilter(
            RequestMatcher requiresAuthenticationRequestMatcher) {
        super(requiresAuthenticationRequestMatcher);
    }
    
    @Override
    public Authentication attemptAuthentication(HttpServletRequest request,
            HttpServletResponse response) throws AuthenticationException,
            IOException, ServletException {
        Authentication auth = new TokenAuthentication("-1");
        try {
            Map<String, String[]> params = request.getParameterMap();
            if (!params.isEmpty() && params.containsKey("auth_token")) {
                String token = params.get("auth_token")[0];
                if (token != null) {
                    auth = new TokenAuthentication(token);
                }
            }
            return this.getAuthenticationManager().authenticate(auth);
        } catch (AuthenticationException ae) {
            unsuccessfulAuthentication(request, response, ae);
        }
        return auth;
    }}
    

    and my http security is:

        public static class SecurityConfigForRS extends
            WebSecurityConfigurerAdapter {
    
        @Autowired
        TokenAuthenticationProvider tokenAuthenticationProvider;
    
        @Override
        protected void configure(AuthenticationManagerBuilder auth)
                throws Exception {
            auth.authenticationProvider(tokenAuthenticationProvider);
        }
    
        @Bean
        @Override
        public AuthenticationManager authenticationManagerBean()
                throws Exception {
            return super.authenticationManagerBean();
        }
    
        @Bean
        protected AbstractAuthenticationProcessingFilter getTokenAuthFilter()
                throws Exception {
            TokenAuthenticationProcessingFilter tapf = new TokenAuthenticationProcessingFilter(
                    new RegexRequestMatcher("^/rest.*", null));
            tapf.setAuthenticationManager(authenticationManagerBean());
            return tapf;
        }
    
        @Override
        protected void configure(HttpSecurity http) throws Exception {
            super.configure(http);
    
            http.regexMatcher("^/rest.*")
                    .addFilterAfter(getTokenAuthFilter(),
                            BasicAuthenticationFilter.class).csrf().disable();
    
        }
    }
    

    The filter chain order does matter! I placed it after BasicAuthenticationFilter and it works fine. Of course there might be a better solution but for now this works!