简体   繁体   中英

How to throw Custom Exception from JWT Authentication Filter

I am using Spring Boot with JWT and spring security in Project. Spring Boot: 2.3.5.RELEASE JJWT Verison: 0.11.2

I have created the JWT Authentication Filter class for interrupting requests.If the request contains JWT token in headers then parse the token, get roles, and set authentication object in the spring security context. My Authentication Filter class as follows

import java.io.IOException;
import java.util.Arrays;
import java.util.Collection;
import java.util.stream.Collectors;

import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.apache.commons.lang.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.web.authentication.WebAuthenticationDetailsSource;
import org.springframework.web.filter.OncePerRequestFilter;

import io.jsonwebtoken.Claims;

public class JwtAuthenticationFilter extends OncePerRequestFilter {

    @Autowired
    private UserDetailsService userDetailsService;

    @Autowired
    private JwtTokenUtil jwtTokenUtil;

    @Override
    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain)
            throws ServletException, IOException {

        String header = request.getHeader(JwtConstant.AUTHORIZATION);

        if (StringUtils.isNotBlank(header) && header.startsWith(JwtConstant.BEARER)) {

            String authToken = header.replace(JwtConstant.BEARER, "");
            Claims claims = jwtTokenUtil.getJwtClaims(authToken);

            String username = claims.getSubject();

            UserDetails userDetails = userDetailsService.loadUserByUsername(username);
            UsernamePasswordAuthenticationToken authentication = new UsernamePasswordAuthenticationToken(userDetails,
                    "", getAuthoritiesFromString(claims));

            authentication.setDetails(new WebAuthenticationDetailsSource().buildDetails(request));

            logger.info("authenticated user " + username + ", setting security context");
            SecurityContextHolder.getContext().setAuthentication(authentication);

        }

        filterChain.doFilter(request, response);
    }

    private Collection<? extends GrantedAuthority> getAuthoritiesFromString(Claims claims) {

        return Arrays.stream(claims.get(JwtConstant.AUTHORITIES).toString().split(",")).map(SimpleGrantedAuthority::new)
                .collect(Collectors.toList());
    }

}

Token Parsing Method as follows:

public Claims getJwtClaims(String token) {

        Claims claims = null;

        try {
            claims = Jwts.parserBuilder().setSigningKey(getPublicKey()).build().parseClaimsJws(token).getBody();
        } catch (ExpiredJwtException e) {
            throw new CustomException(env.getProperty(ExceptionMessage.TOKEN_EXPIRED),e, ErrorCode.TOKEN_EXPIRE);
        } catch (SignatureException | MalformedJwtException e) {
            throw new CustomException(env.getProperty(ExceptionMessage.TOKEN_INVALID),e, ErrorCode.TOKEN_INVALID);
        } catch (Exception e) {
            throw new CustomException(env.getProperty(ExceptionMessage.TOKEN_PARSING),e, ErrorCode.INTERNAL_SERVER_ERROR);
        }

        return claims;
    } 

When parsing token if the oken is invalid or expired, then I am throwing Custom Exception which have Integer errorCode.

When request is containing expired or invalid token, first it goes to jwt authentication filter class. While parsing token I am getting following response:

{
    "timestamp": "2020-11-30T08:28:40.319+00:00",
    "status": 500,
    "error": "Internal Server Error",
    "message": "Token Expired",
    "path": "/api/users/profile"
}

As This exception is thrown from servlet filter It is not going to @RestControllerAdvise and its throwing inbuilt exception response. I want to throw my Custom Exception class with error code in this case. I tried out different solutions but not able to solve my problem. Is there any way to throw custom error code from this case.

I want below exception response,Which is my custom response class:

{
    "message": "Token Expired",
    "code": 101,
    "error": true,
    "timestamp": "2020-11-30T08:31:46.911+00:00"
}

Create a customerErrorController for "/error" like:

@RestController
public class CustomErrorController implements ErrorController {
    
    private static final String PATH = "/error";
    
    @RequestMapping(PATH)
    public ResponseEntity<ErrorDetails> handleError(final HttpServletRequest request,
            final HttpServletResponse response) throws Throwable {
        throw (Throwable) request.getAttribute("javax.servlet.error.exception");
    }
    
    @Override
    public String getErrorPath() {
        return PATH;
    }
}

And then @RestControllerAdvise will be able to pick exceptions.

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