如何正确处理 JwtException.?

当令牌过期时,我收到这样的错误

io.jsonwebtoken.JwtException: JWT expired at 2020-09-18T19:08:08Z. Current time: 2020-09-22T20:26:51Z, a difference of 350323563 milliseconds.  Allowed clock skew: 0 milliseconds.

我创建了一个实现 AuthenticationEntryPoint 的类

@Component
public class CustomAuthenticationEntryPoint implements AuthenticationEntryPoint {

    @Override
    public void commence(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse,
                         AuthenticationException e) throws IOException, ServletException {
        ObjectMapper mapper = new ObjectMapper();
        ErrorDetails errorDetails = ErrorDetails.builder()
                .details(String.valueOf(e.getClass()))
                .message("JWT has expired")
                .timestamp(DateTimeFormatter.ofPattern("MM-dd-yyyy HH:mm:ss", Locale.ENGLISH)
                        .format(LocalDateTime.now()))
                .build();
        httpServletResponse.setStatus(HttpStatus.UNAUTHORIZED.value());
        httpServletResponse.setContentType("application/json");
        httpServletResponse.setCharacterEncoding("UTF-8");
        httpServletResponse.getWriter().write(mapper.writeValueAsString(errorDetails));
    }

在邮递员我得到

{
    "timestamp": "09-22-2020 20:26:51",
    "message": "JWT has expired",
    "details": "class org.springframework.security.authentication.InsufficientAuthenticationException"
}

配置方法表单 SecurityConfig

@Override
    protected void configure(HttpSecurity http) throws Exception {
        http
                .cors()
                .and()
                .csrf()
                .disable()
                .sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS)
                .and()
                .exceptionHandling().authenticationEntryPoint(entryPoint)
                .and()
                .addFilterAfter(new JwtAuthorizationFilter(authenticationManager(), secret),
                        UsernamePasswordAuthenticationFilter.class)
                .authorizeRequests()
                .antMatchers("/api/credit/**").hasRole("USER")
                .antMatchers("/api/auth/login").permitAll()
                .anyRequest().denyAll();
        http
                .headers()
                .addHeaderWriter(new StaticHeadersWriter("Content-Type", "application/json"));
    }

有人可以建议我如何正确处理我在 IDE 中遇到的异常。
解决方案
在 JwtAuthorizationFilter

catch (ExpiredJwtException e) {
            request.setAttribute("expired", e.getMessage());
        }

在 CustomAuthenticationEntryPoint

if(httpServletRequest.getAttribute("expired") != null){
            errorDetails.setMessage(String.valueOf(httpServletRequest.getAttribute("expired")));
        }
stack overflow How to properly handle JwtException.?
原文答案

答案:

作者头像

Spring Security 5.1+ 有 built-in support for JWTs

您可以配置 Spring Security 来查找 JWT,而不是连接您自己的自定义过滤器:

http
    // ...
    .oauth2ResourceServer((oauth2) -> oauth2
        .authenticationEntryPoint(myCustomEntryPoint)
        .jwt()
    )
    // ... no need for a custom filter

由于您有自定义的方式来处理 JWT,因此您可以发布 your own implementation of JwtDecoder

@Bean
public JwtDecoder jwtDecoder() {
    return (encodedJwt) -> {
        // verify the JWT
    }
}

Spring Security 的不记名令牌过滤器将在适当的时间调用您的 AuthenticationEntryPoint 并减少您应用程序的一些自定义。

作者头像

下面是我们如何捕获 JwtException (io.jsonwebtoken.ExpiredJwtException)。

关键部分是:

  • JWTFilter 之前配置 UsernamePasswordAuthenticationFilter
  • 解析 JWT 令牌并捕获 ExpiredJwtException
  • ExpiredJwtException 包装在 AuthenticationException 的实现之一中并重新抛出,以便它将在您的 CustomAuthenticationEntryPoint 中处理。请注意, org.springframework.security.web.access.ExceptionTranslationFilter#handleSpringSecurityException 仅针对 AuthenticationEntryPointAuthenticationException 委托给 AccessDeniedException

JWTFilter

@Slf4j
public class JWTFilter extends GenericFilterBean {

    @Override
    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) {
        HttpServletRequest httpServletRequest = (HttpServletRequest) servletRequest;

        String jwt = resolveToken(httpServletRequest);

        try {
            Claims claims = Jwts.parserBuilder().setSigningKey(key).build()
                    .parseClaimsJws(jwt).getBody();

            //everything is valid !!, let's login
            Authentication authentication = getAuthentication(claims);
            SecurityContextHolder.getContext().setAuthentication(authentication);
        } catch (ExpiredJwtException e) {
            log.error("Expired JWT token.", e);
            //HANDLE IT HERE::::: wrap ExpiredJwtException in AuthenticationException and rethrow Exception
           throw new CredentialsExpiredException("Expired jwt credentials ", e);

        } catch (OtherExceptions e) {
            log.info("JWT token compact of handler are invalid.");
            log.trace("JWT token compact of handler are invalid trace: ", e);
        }

        //finally filter it through
        filterChain.doFilter(servletRequest, servletResponse);
    }

}
作者头像

// 你可以简单地用 try 块包围你的 Filter 类并捕获这个 // 异常

 catch (Exception exception){
                log.error("Error logging in : {} ", exception.getMessage());
                response.setHeader("error",exception.getMessage());
                response.setStatus(FORBIDDEN.value());
                Map<String,String > error = new HashMap<>();
                error.put("error_message",exception.getMessage());
                response.setContentType(APPLICATION_JSON_VALUE);
                new ObjectMapper().writeValue(response.getOutputStream(),error);
            }

// 然后在你验证令牌的 util 类中 catch //this

 public Boolean validateToken(String token, UserDetails userDetails, HttpServletRequest request)  {
        try {
            final String username = extractUsername(token);
            log.info("Username " + username.equals(userDetails.getUsername()));
            return (username.equals(userDetails.getUsername()) && !isTokenExpired(token));
        } catch (ExpiredJwtException expiredJwtException) {
            log.info("Utils :: validateToke :: token Exception -> expired!");
            request.setAttribute("expired",expiredJwtException.getMessage());
            throw new ExpiredJwtException(expiredJwtException.getHeader(), expiredJwtException.getClaims(), "Expired JWT token");

// 完成这些步骤后,您将获得所需的响应。