Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Spring security custom token filter

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?

like image 599
Federico Lenzi Avatar asked Mar 12 '14 19:03

Federico Lenzi


2 Answers

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()  
like image 154
Eugen Halca Avatar answered Sep 21 '22 21:09

Eugen Halca


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

like image 21
kedzi Avatar answered Sep 20 '22 21:09

kedzi