I created two filters to perform authentication with the JWT Token. I would like to handle a JWT exception in the first one: exceptionTokenVerificationHandlerFilter and check if all is OK within the second: jwtTokenFilter. If authentication fails because of the expiration date, the process back to the first filter and sends a response to the front with an access denied message.
I used this answer as support for my app : https://github.com/szerhusenBC/jwt-spring-security-demo/issues/63#issuecomment-377012514
My classes :
WebSecurityConfig
@Override
protected void configure(HttpSecurity http) throws Exception {
http
.httpBasic().disable()
.addFilterBefore(exceptionTokenVerificationHandlerFilter, JwtTokenFilter.class)
.addFilterBefore(jwtTokenFilter, UsernamePasswordAuthenticationFilter.class)
.sessionManagement()
.sessionCreationPolicy(SessionCreationPolicy.STATELESS)
.and()
.cors().and()
.csrf().disable()
.authorizeRequests() // .antMatchers("/**")
.antMatchers("/login/**", "/register/**").permitAll()
.antMatchers("/admin/**").hasRole("ADMIN")
.anyRequest().authenticated()
.and()
//.csrfTokenRepository(CookieCsrfTokenRepository.withHttpOnlyFalse());
.addFilterAt(customUsernamePasswordAuthenticationFilter, UsernamePasswordAuthenticationFilter.class)
/*.formLogin()
.loginPage("http://localhost:4200/login")//.failureUrl("/login-error")
.loginProcessingUrl("/login")
.usernameParameter("email")
.successHandler(customAuthenticationSuccessHandler)
.and()*/
.logout()
.permitAll();
}
ExceptionTokenVerificationHandlerFilter
package app.shellx.security;
import java.io.IOException;
import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.HttpStatus;
import org.springframework.web.filter.OncePerRequestFilter;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import io.jsonwebtoken.JwtException;
@Configuration
public class ExceptionTokenVerificationHandlerFilter extends OncePerRequestFilter {
@Override
public void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
try {
filterChain.doFilter(request, response);
} catch (JwtException e) {
String message;
switch(e.getClass().getName()) {
case "ExpiredJwtException":
message = "token-expired";
break;
default:
message = e.getMessage();
break;
}
response.setStatus(HttpStatus.BAD_REQUEST.value());
response.getWriter().write(convertObjectToJson(message));
}
}
private String convertObjectToJson(Object object) throws JsonProcessingException {
if (object == null) {
return null;
}
ObjectMapper mapper = new ObjectMapper();
return mapper.writeValueAsString(object);
}
}
JwtTokenFilter
package app.shellx.security;
import java.io.IOException;
import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.web.filter.GenericFilterBean;
@Configuration
public class JwtTokenFilter extends GenericFilterBean {
private JwtTokenProvider jwtTokenProvider;
public JwtTokenFilter(JwtTokenProvider jwtTokenProvider) {
this.jwtTokenProvider = jwtTokenProvider;
}
@Override
public void doFilter(ServletRequest req, ServletResponse res, FilterChain filterChain) throws IOException, ServletException {
String token = jwtTokenProvider.resolveToken((HttpServletRequest) req);
if (token != null && jwtTokenProvider.validateToken(token)) {
Authentication auth = token != null ? jwtTokenProvider.getAuthentication(token) : null;
SecurityContextHolder.getContext().setAuthentication(auth);
}
filterChain.doFilter(req, res);
}
}
So can I add in the filterchain two custom filters one after the other ?
I receive at the moment the following error message from compilator :
Caused by: java.lang.IllegalArgumentException: Cannot register after unregistered Filter class app.shellx.security.JwtTokenFilter
I solved myself the problem by using @Order
annotation on both filter classes.
@Order(1)
for ExceptionTokenVerificationHandlerFilter
and @Order(2)
for JwtTokenFilter
.
I also changed second parameter of the first addFilterBefore()
by UsernamePasswordAuthenticationFilter.class
It seems that we can't put a custom filter as the second parameter in this method.