【问题标题】:How to properly handle JwtException.?如何正确处理 JwtException.?
【发布时间】:2020-09-26 00:48:28
【问题描述】:

令牌过期时出现这样的错误

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")));
        }

【问题讨论】:

  • 将您的代码发布到您创建 jwt 令牌的位置。这是因为过期时间。
  • @SurajGautam 在过期后尚未创建新令牌。我不知道我是否会这样做,因为我在 ApiGateway 中检查令牌并在另一个服务中创建令牌
  • 如果令牌可以自动更新,您应该捕获异常并为用户更新令牌。否则,只需返回登录页面供用户重新登录。
  • 我明白,但现在我想知道如何处理这样的错误
  • 好的。请参阅下面的答案。

标签: spring spring-security jwt


【解决方案1】:

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 并减少您应用的一些自定义。

【讨论】:

    【解决方案2】:

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

    关键部分是:

    • UsernamePasswordAuthenticationFilter之前配置JWTFilter
    • 解析JWT Token并捕获ExpiredJwtException
    • ExpiredJwtException 包装在AuthenticationException 的实现之一中并重新抛出,以便在您的CustomAuthenticationEntryPoint 中处理它。请注意,org.springframework.security.web.access.ExceptionTranslationFilter#handleSpringSecurityException 仅代表 AuthenticationEntryPoint 用于 AuthenticationExceptionAccessDeniedException

    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);
        }
    
    
    }
    

    【讨论】:

    • thx,但我不明白“在 AuthenticationException 中包装 ExpiredJwtException”是什么意思
    • 我还是做不到。` catch (ExpiredJwtException e) { throw new AuthenticationException(e.getMessage()); //抛出新的CustomAuthenticationException(e.getMessage()); }catch (Exception e){ throw e; } `我一直得到相同的日志
    • 你可以分享你的项目吗?
    • 您需要在 com.creditApp.security.JwtAuthorizationFilter#getAuthentication 上拥有} catch (ExpiredJwtException e) { throw new CredentialsExpiredException("Expired jwt credentials ", e); }
    猜你喜欢
    • 1970-01-01
    • 2016-04-09
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多