Spring security custom token filter
Solution 1
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()
Solution 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.
Federico Lenzi
Updated on February 20, 2020Comments
-
Federico Lenzi about 4 years
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?