9

I'm trying to perform a custom filter to get a token and validate it. I'm following the approach in this response.

This is the relevant configuration:

SecurityConfig:

@Configuration
@EnableWebSecurity
@ComponentScan(basePackages = {"com.company.app"})
public class SecurityConfig extends WebSecurityConfigurerAdapter {

@Inject
AuthenticationTokenFilter authenticationTokenFilter;

@Inject
TokenAuthenticationProvider tokenAuthenticationProvider;

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http
            .addFilterBefore(authenticationTokenFilter, BasicAuthenticationFilter.class)
                .antMatcher("/*")
                .authenticationProvider(tokenAuthenticationProvider)
                .authorizeRequests()
                    .anyRequest().authenticated();
    }

}

AuthenticationTokenFilter:

@Component
public class AuthenticationTokenFilter implements Filter {

private static final Logger logger = LoggerFactory.getLogger(AuthenticationTokenFilter.class);

@Override
public void init(FilterConfig fc) throws ServletException {
    logger.info("Init AuthenticationTokenFilter");
}

@Override
public void doFilter(ServletRequest req, ServletResponse res, FilterChain fc) throws IOException, ServletException {
    SecurityContext context = SecurityContextHolder.getContext();
    if (context.getAuthentication() != null && context.getAuthentication().isAuthenticated()) {
        // do nothing
    } else {
        Map<String,String[]> params = req.getParameterMap();
        if (!params.isEmpty() && params.containsKey("auth_token")) {
            String token = params.get("auth_token")[0];
            if (token != null) {
                Authentication auth = new TokenAuthentication(token);
                SecurityContextHolder.getContext().setAuthentication(auth);
            }
        }
    }

    fc.doFilter(req, res);
}

@Override
public void destroy() {

}
}

TokenAuthentication:

public class TokenAuthentication implements Authentication {
private String token;

public TokenAuthentication(String token) {
    this.token = token;
}
@Override
public Collection<? extends GrantedAuthority> getAuthorities() {
    return new ArrayList<GrantedAuthority>(0);
}
@Override
public Object getCredentials() {
    return token;
}
@Override
public Object getDetails() {
    return null;
}
@Override
public Object getPrincipal() {
    return null;
}
@Override
public boolean isAuthenticated() {
    return false;
}
@Override
public void setAuthenticated(boolean isAuthenticated) throws IllegalArgumentException {
}
@Override
public String getName() {
    return null;
}
}

TokenAuthenticationProvider:

@Component
public class TokenAuthenticationProvider implements AuthenticationProvider {

private static final Logger logger = LoggerFactory.getLogger(TokenAuthenticationProvider.class);

@Override
public Authentication authenticate(Authentication auth) throws AuthenticationException {
    if (auth.isAuthenticated())
        return auth;

    String token = auth.getCredentials().toString();
    User user = userSvc.validateApiAuthenticationToken(token);
    if (user != null) {
        auth = new PreAuthenticatedAuthenticationToken(user, token);
        auth.setAuthenticated(true);
        logger.debug("Token authentication. Token: ");
    } else
        throw new BadCredentialsException("Invalid token " + token);
    return auth;
}

@Override
public boolean supports(Class<?> aClass) {
    return true;
}

}

But it's like the AuthenticationTokenFilter is not being added to the chain. Debugging I can see that when I do a call it enters to the SecurityConfig and configure method but not to the filter. What is missing?

Community
  • 1
  • 1
Federico Lenzi
  • 1,632
  • 4
  • 18
  • 34
  • do you have any other security config classes ? – Eugen Halca Mar 12 '14 at 19:14
  • @Eugen Halca no, is something missing ? – Federico Lenzi Mar 12 '14 at 19:17
  • it looks like it should work, i had some strange behaviours like your, it was with `@Order` annotation on security config classes – Eugen Halca Mar 12 '14 at 19:19
  • at the first call it enters to the configure method(but only once, the next calls enters directly to the controller). The filter is being ignored, I don't know if the HttpSecurity configuration is correct – Federico Lenzi Mar 12 '14 at 19:24
  • Have you got import org.springframework.security.web.context.AbstractSecurityWebApplicationInitializer; public class SecurityWebAppInitializer extends AbstractSecurityWebApplicationInitializer { } in your project? – Alexander Burakevych May 28 '14 at 05:49
  • 2
    when will TokenAuthenticationProviderr will be called? Should AuthenticationTokenFilter and TokenAuthenticationProviderr be called on each request?? – Taran Apr 11 '16 at 21:52
  • when `authenticate` method will be called is this only once or with every request? –  Aug 16 '17 at 17:07

4 Answers4

4

try to disable anonymous authentication and change to fully authentication to your security rule.

something like this :

http
    .addFilterBefore(authenticationTokenFilter, BasicAuthenticationFilter.class)
                    .antMatcher("/token")
                    .authenticationProvider(tokenAuthenticationProvider)
                    .authorizeUrls().anyRequest().fullyAuthenticated()
    .and()
                    .anonymous().disable()  
Eugen Halca
  • 1,775
  • 2
  • 13
  • 26
2

What you are missing is

<filter>
       <filter-name>springSecurityFilterChain</filter-name>
       <filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>
</filter>
<filter-mapping>
       <filter-name>springSecurityFilterChain</filter-name>
       <url-pattern>/*</url-pattern>
</filter-mapping>

in your web.xml or equivalent for intializers on your classpath:

import org.springframework.security.web.context.AbstractSecurityWebApplicationInitializer;

@Order(value = 1)
public class SecurityInitializer extends AbstractSecurityWebApplicationInitializer {
}

This is separate from your WebApplicationInitializer. Note that:

  • your SecurityConfig (or anything annotated with @EnableWebSecurity) must be defined in the root context (not the dispatcher context)
  • you probably should understand order of initialization (I am not sure if I do):

"Ordering of WebApplicationInitializer"
If any servlet Filter mappings are added after AbstractSecurityWebApplicationInitializer is invoked, they might be accidentally added before springSecurityFilterChain. Unless an application contains Filter instances that do not need to be secured, springSecurityFilterChain should be before any other Filter mappings. The @Order annotation can be used to help ensure that any WebApplicationInitializer is loaded in a deterministic order.

Example:

@Order(value = 10)
public class AppWebAppInitializer extends AbstractAnnotationConfigDispatcherServletInitializer {

  @Override
  protected Class<?>[] getRootConfigClasses() {
      return new Class<?>[] { AppConfig.class, SecurityConfig.class };
  }

  @Override
  protected Class<?>[] getServletConfigClasses() {
      return new Class<?>[] { RestConfig.class };
  }

  @Override
  protected String[] getServletMappings() {
      return new String[] { "/rest/*"};
  }
}


To summarize, from Spring documentation:

When using servlet filters, you obviously need to declare them in your web.xml, or they will be ignored by the servlet container. In Spring Security, the filter classes are also Spring beans defined in the application context and thus able to take advantage of Spring's rich dependency-injection facilities and lifecycle interfaces. Spring's DelegatingFilterProxy provides the link between web.xml and the application context.

The Security Filter Chain

kedzi
  • 89
  • 1
  • 5
0

Old post, but I think authenticationProvider() needs to come BEFORE "addBeforeFilter". Not sure if it will make a difference today, but it might be important. It may not matter as much.

Also try add this on your configuration class to solve the issue:

@Order(SecurityProperties.ACCESS_OVERRIDE_ORDER)
public class SecurityConfig extends WebSecurityConfigurerAdapter {
Dexter
  • 6,170
  • 18
  • 74
  • 101
0

FYI: using both the @Component on the Filter and @Inject with addFilterBefore will apply the filter twice! In your case it is just more processing time, so you wont see any errors. But if you are injecting lets say a metrics filter, then you will be getting wrong metrics.