简体   繁体   中英

Add nested custom filters in Spring Security filterchain

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.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM