Search code examples
spring-securityspring-java-config

spring filters applies to all api urls Errors


i am new to spring security,problem is that filter is applied to all request urls i-e. /api/user/signup. i want to exclude /api/user/signup path to go through the filter. i even tried following

web.ignoring()
            .antMatchers("/api/user/**")
            .antMatchers("/api/user/signup")

but filter again applied to it. Filter requires authkey token and signup request obvously will have no token. and it will through exception when it don't finds the token.Following is spring security java config class

@Configuration
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
@Autowired
AuthenticationTokenProcessingFilter authenticationTokenFilter;
@Override
public void configure(WebSecurity web) throws Exception {
    web.ignoring()
    .antMatchers("/api/user/**")
    .antMatchers("/api/user/signup")

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


    http
    .csrf().disable()
    .authorizeRequests()   

    //allow anonymous POSTs to login
    .antMatchers("/api/user/signup").permitAll()

    //allow anonymous GETs to API
    .antMatchers(HttpMethod.GET, "/api/**").permitAll()
    .and()
    .sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS)     
    .and()
     .addFilterBefore(authenticationTokenFilter, UsernamePasswordAuthenticationFilter.class)    
      ;
}

and here is my token filter class

@Component
public class AuthenticationTokenProcessingFilter extends GenericFilterBean {

  //  @Autowired UserService userService;
   @Autowired 
   TokenHandler tokenUtils;
   @Autowired
    AuthenticationManager authManager;

    public AuthenticationTokenProcessingFilter(AuthenticationManager authManager) {
        this.authManager = authManager;
    }

    public AuthenticationTokenProcessingFilter() {
        super();
        // TODO Auto-generated constructor stub
    }

    @Override
    public void doFilter(ServletRequest request, ServletResponse response,
            FilterChain chain) throws IOException, ServletException {
        @SuppressWarnings("unchecked")

        HttpServletRequest req = (HttpServletRequest) request;

        SecurityContext context = SecurityContextHolder.getContext();

        if (context.getAuthentication() != null && context.getAuthentication().isAuthenticated()) {
            // do nothing
        }else {

            //System.out.println("Not Authenticated");
        if(req != null && req.getHeader("authKey") != null && req.getHeader("authKey").length() >0 ) {
            String token = req.getHeader("authKey"); 
           System.out.println("Found Token:"+req.getHeader("authKey"));
            // validate the token
            User userDetails = tokenUtils.parseUserFromToken(token);

                     List<GrantedAuthority> grantedAuths = new ArrayList<>();
                     grantedAuths.add(new SimpleGrantedAuthority("ROLE_USER"));
                     Authentication  authentication = 
                        new UsernamePasswordAuthenticationToken(userDetails.getEmail(), userDetails.getPassword(),grantedAuths);

                // set the authentication into the SecurityContext

                SecurityContextHolder.getContext().setAuthentication(authentication);         


           System.out.println("Is Authenticated:?"+  context.getAuthentication().isAuthenticated());
        // continue thru the filter chain
           chain.doFilter(request, response);
           System.out.println(" request is delegeted");
        }else{ 

                // Do your business stuff here for all paths other than /specialpath.
                System.out.println(" Token Not Found");
                throw new ServletException("Token not found in Request");           

        }

    }
}

following is error log when /api/user/signup comes

Token Not Found
2015-05-17 09:38:36.742 ERROR 5096 --- [nio-8090-exec-1] o.a.c.c.C.[.[.[/].[dispatcherServlet]    : Servlet.service() for servlet [dispatcherServlet] in context with path [] threw exception [Token not found in Request] with root cause

javax.servlet.ServletException: Token not found in Request
    at com.bitsparlour.sadaf.cloud.application.AuthenticationTokenProcessingFilter.doFilter(AuthenticationTokenProcessingFilter.java:93)
    at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:239)
    at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:206)
    at org.springframework.web.filter.CharacterEncodingFilter.doFilterInternal(CharacterEncodingFilter.java:85)
    at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107)
    at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:239)
    at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:206)
    at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:219)
    at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:106)
    at org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:501)
    at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:142)
    at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:79)
    at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:88)
    at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:537)
    at org.apache.coyote.http11.AbstractHttp11Processor.process(AbstractHttp11Processor.java:1085)
    at org.apache.coyote.AbstractProtocol$AbstractConnectionHandler.process(AbstractProtocol.java:658)
    at org.apache.coyote.http11.Http11NioProtocol$Http11ConnectionHandler.process(Http11NioProtocol.java:222)
    at org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.doRun(NioEndpoint.java:1556)
    at org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.run(NioEndpoint.java:1513)
    at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1142)
    at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617)
    at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61)
    at java.lang.Thread.run(Thread.java:745)

Solution

  • The solution is to mimic the good old namespace way of having multiple <http ...> blocks. With Java configuration, you can also have multiple classes extending WebSecurityConfigurerAdapter. Spring Security Reference Manual has an example for that. Extracts :

    @EnableWebSecurity
    public class MultiHttpSecurityConfig {
        @Autowired
        public void configureGlobal(AuthenticationManagerBuilder auth) { 1
            ...
        }
    
        @Configuration
        @Order(1)                                                        2
        public static class ApiWebSecurityConfigurationAdapter extends WebSecurityConfigurerAdapter {
            protected void configure(HttpSecurity http) throws Exception {
                http
                    .antMatcher("/api/**")                               3
                    ...
            }
        }
    
        @Configuration                                                   4
        public static class FormLoginWebSecurityConfigurerAdapter extends WebSecurityConfigurerAdapter {
    
            @Override
            protected void configure(HttpSecurity http) throws Exception {
                http
                    .formLogin()
                    ...
            }
        }
    }
    

    Notes :

    1. Configure Authentication as normal
    2. Create an instance of WebSecurityConfigurerAdapter that contains @Order to specify which WebSecurityConfigurerAdapter should be considered first.
    3. The http.antMatcher states that this HttpSecurity will only be applicable to URLs that start with /api/
    4. Create another instance of WebSecurityConfigurerAdapter. If the URL does not start with /api/ this configuration will be used (no @Order defaults to last)