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?
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()
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:
"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
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With