在 Spring Boot 中验证 JWT 时如何处理 InvalidSignatureException?

Posted

技术标签:

【中文标题】在 Spring Boot 中验证 JWT 时如何处理 InvalidSignatureException?【英文标题】:How can I handle InvalidSignatureException while validating JWT in Spring Boot? 【发布时间】:2020-06-03 20:54:19 【问题描述】:

我正在开发自己的Spring Boot身份验证服务器和资源服务器,它们工作正常。 身份验证服务器提供 RS256 JWS 令牌,资源服务器能够验证令牌。

我很高兴,我想知道如果我使用不同的 RSA 密钥对来验证 JWS 令牌会发生什么。结果出来了:

org.springframework.security.jwt.crypto.sign.InvalidSignatureException: RSA Signature did not match content
    at org.springframework.security.jwt.crypto.sign.RsaVerifier.verify(RsaVerifier.java:55)
    at org.springframework.security.jwt.JwtImpl.verifySignature(JwtHelper.java:287)
    at org.springframework.security.oauth2.provider.token.store.jwk.JwkVerifyingJwtAccessTokenConverter.decode(JwkVerifyingJwtAccessTokenConverter.java:122)
    at org.springframework.security.oauth2.provider.token.store.JwtTokenStore.convertAccessToken(JwtTokenStore.java:88)
    at org.springframework.security.oauth2.provider.token.store.JwtTokenStore.readAccessToken(JwtTokenStore.java:80)
    at org.springframework.security.oauth2.provider.token.store.jwk.JwkTokenStore.readAccessToken(JwkTokenStore.java:212)
    at org.springframework.security.oauth2.provider.token.DefaultTokenServices.loadAuthentication(DefaultTokenServices.java:229)
    at org.springframework.security.oauth2.provider.token.DefaultTokenServices$$FastClassBySpringCGLIB$$5a1f25c.invoke(<generated>)
    at org.springframework.cglib.proxy.MethodProxy.invoke(MethodProxy.java:204)
    at org.springframework.aop.framework.CglibAopProxy$DynamicAdvisedInterceptor.intercept(CglibAopProxy.java:684)
    at org.springframework.security.oauth2.provider.token.DefaultTokenServices$$EnhancerBySpringCGLIB$$14463cf2.loadAuthentication(<generated>)
    at org.springframework.security.oauth2.provider.authentication.OAuth2AuthenticationManager.authenticate(OAuth2AuthenticationManager.java:83)
    at org.springframework.security.oauth2.provider.authentication.OAuth2AuthenticationProcessingFilter.doFilter(OAuth2AuthenticationProcessingFilter.java:150)
    at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:334)
...

响应是 正文:

<!doctype html>
<html lang="en">

<head>
    <title>HTTP Status 500 – Internal Server Error</title>
    <style type="text/css">
        h1 
            font-family: Tahoma, Arial, sans-serif;
            color: white;
            background-color: #525D76;
            font-size: 22px;
        

        h2 
            font-family: Tahoma, Arial, sans-serif;
            color: white;
            background-color: #525D76;
            font-size: 16px;
        

        h3 
            font-family: Tahoma, Arial, sans-serif;
            color: white;
            background-color: #525D76;
            font-size: 14px;
        

        body 
            font-family: Tahoma, Arial, sans-serif;
            color: black;
            background-color: white;
        

        b 
            font-family: Tahoma, Arial, sans-serif;
            color: white;
            background-color: #525D76;
        

        p 
            font-family: Tahoma, Arial, sans-serif;
            background: white;
            color: black;
            font-size: 12px;
        

        a 
            color: black;
        

        a.name 
            color: black;
        

        .line 
            height: 1px;
            background-color: #525D76;
            border: none;
        
    </style>
</head>

<body>
    <h1>HTTP Status 500 – Internal Server Error</h1>
</body>

</html>

我想将此错误处理为自定义 JSON 错误正文,或者至少看起来与普通的 AuthenticationException 相同

例如


    "error": "invalid_token",
    "error_description": "Invalid JWT/JWS: RSA Signature did not match content"

我的 Spring/Boot 依赖项:

plugins 
    id 'org.springframework.boot' version '2.2.4.RELEASE'
    ...


dependencies 
    implementation 'org.springframework.boot:spring-boot-starter'
    testImplementation('org.springframework.boot:spring-boot-starter-test') 
        exclude group: 'org.junit.vintage', module: 'junit-vintage-engine'
    
    compile("org.springframework.boot:spring-boot-starter-security")
    compile("org.springframework.security.oauth.boot:spring-security-oauth2-autoconfigure:2.1.0.RELEASE")
    compile('org.springframework.boot:spring-boot-starter-web')
    testCompile('org.springframework.boot:spring-boot-starter-web')
    compile("org.springframework.cloud:spring-cloud-starter-sleuth")
    compile("org.springframework.security:spring-security-jwt")
    ...

我尝试过的:

@ControllerAdvice中处理(从***的其他答案来看,应该是请求还没有传入MVC,所以没用) 通过ResourceServerConfigurerAdapter.configure(HttpSecurity)注册自定义AuthenticationEntryPoint(不知道为什么没用,试过了) 编辑 尝试了@Jazzepi 的建议,Implementing HandlerExceptionResolver or extends AbstractHandlerExceptionResolver with method:
@Override
    public ModelAndView resolveException(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) 
        if (ex instanceof InvalidSignatureException) 
            ModelAndView mav = new ModelAndView(new MappingJackson2JsonView());
            mav.addObject("key", "value");
            return mav;
        
        return null;
    

然后尝试通过WebMvcConfigurator.extendHandlerExceptionResolvers(List&lt;HandleExceptionResolvers&gt;)注册(没办法,还是输出和上面一样的响应)


编辑:

如何构建和解码令牌? 完全遵循本教程:https://www.baeldung.com/spring-security-oauth2-jws-jwk

如何重演这种情况?

    启动身份验证和资源服务器。 从认证服务器获取token,在资源服务器中使用一次token,使资源服务器将公钥缓存在本地 更改授权服务器中的密钥库文件并重新启动 从重新启动的身份验证服务器获取新令牌 在资源服务器中使用新令牌

【问题讨论】:

请添加有关如何构建和解码令牌的代码 感谢您的回复。已更新问题。 你弄明白了吗?我有同样的情况,exceptionHandler 将其作为 RuntimeException 处理,状态为 500 :( 【参考方案1】:

Spring Boot 带有几个类,它们将异常映射到在抛出这些异常时运行的方法。

https://docs.spring.io/spring-framework/docs/current/javadoc-api/org/springframework/web/servlet/config/annotation/WebMvcConfigurationSupport.html#addDefaultHandlerExceptionResolvers-java.util.List-

向这个异常解析器链注册一个 HandlerExceptionResolverComposite:

ExceptionHandlerExceptionResolver 用于处理异常通过 ExceptionHandler 方法。

ResponseStatusExceptionResolver 用于注释的异常 响应状态。

DefaultHandlerExceptionResolver 用于解决已知的 Spring 异常 类型

我认为您想创建自己的异常处理程序。它会让你有机会运行你想要的任何代码来响应你的特定异常。大概是想实现这个接口。不过请查看文档,因为 Spring 提供了丰富多样的抽象类,您可以对其进行扩展,这将使您的生活更轻松,而不是从头开始编写接口。

https://docs.spring.io/spring-framework/docs/current/javadoc-api/org/springframework/web/servlet/HandlerExceptionResolver.html

最后,您需要向 Spring 注册您的处理程序。这看起来是个不错的方法。

https://docs.spring.io/spring-framework/docs/current/javadoc-api/org/springframework/web/servlet/config/annotation/WebMvcConfigurer.html#extendHandlerExceptionResolvers-java.util.List-

祝你好运!

为了解决问题,我会在 org/springframework/web/servlet/mvc/support/DefaultHandlerExceptionResolver.java 在 doResolveException 上,看看它是否在那里捕获并处理您的异常(我认为是)。

在 Spring 中,这些解析器之间有一个顺序。您可能希望给您的最高顺序,以便它有机会在默认异常之前处理异常。

@Order(Ordered.HIGHEST_PRECEDENCE)

您可能还希望您的处理程序使用response.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR, ex.getMessage()); sendError 而不是胡乱处理 ModelAndView() 的东西。那是为了告诉 spring 重定向到哪个网页。查看默认处理程序的各个方法,以了解处理错误的不同方式。

【讨论】:

感谢您的帮助!但是好像不行,请参考编辑过的问题 @user2602850 编辑了一些故障排除建议

以上是关于在 Spring Boot 中验证 JWT 时如何处理 InvalidSignatureException?的主要内容,如果未能解决你的问题,请参考以下文章

如何使用 Spring Boot jwt 将身份验证系统连接到 React.js

Spring Boot OAuth2 资源服务器:库如何在没有公钥的情况下验证 JWT 令牌?

Spring boot + JWT 实现安全验证 ---auth0.jwt

Spring Boot OAuth2:从 cookie 中提取 JWT 进行身份验证

在 Spring Boot 安全性中使用公钥/私钥创建和验证 JWT 签名

在 Spring Boot 中验证 JWT 时如何处理 InvalidSignatureException?