Spring Boot OAuth2/OpenID Connect Client 尝试解码 Jwt: Malformed Jwk set 时出错

Posted

技术标签:

【中文标题】Spring Boot OAuth2/OpenID Connect Client 尝试解码 Jwt: Malformed Jwk set 时出错【英文标题】:Spring Boot OAuth2/OpenID Connect Client An error occurred while attempting to decode the Jwt: Malformed Jwk set 【发布时间】:2021-10-22 04:28:43 【问题描述】:

我正在使用 OpenID Connect 为 Spring Boot 应用程序实现单点登录身份验证。我已经在我的应用程序中使用第三方 OpenID Connect 授权服务器配置了 Spring Boot Oauth2 客户端。 对于 Spring,我遵循配置 this。我的客户重定向 uri 已向 OpenID 提供程序注册。 使用上述配置,当用户尝试访问任何受保护的资源时,应用程序将重定向到身份验证服务器登录页面,用户在其中提交登录凭据。此后我的应用程序失败并出现错误

2021-08-20 17:26:07.481 DEBUG [dashboard,,,] 113904 --- [o-8443-exec-161] o.s.web.client.RestTemplate              : HTTP POST https://domain/oauth2/token
2021-08-20 17:26:07.485 DEBUG [dashboard,,,] 113904 --- [o-8443-exec-161] o.s.web.client.RestTemplate              : Accept=[application/json, application/*+json]
2021-08-20 17:26:07.487 DEBUG [dashboard,,,] 113904 --- [o-8443-exec-161] o.s.web.client.RestTemplate              : Writing [grant_type=[authorization_code], code=[BrHZBSzmXT4uvuUHfhyuhnyuhuh], redirect_uri=[https://baseUrl/login/oauth2/code/abc]] as "application/x-www-form-urlencoded;charset=UTF-8"
2021-08-20 17:26:07.902 DEBUG [dashboard,,,] 113904 --- [o-8443-exec-161] o.s.web.client.RestTemplate              : Response 200 OK
2021-08-20 17:26:07.905 DEBUG [dashboard,,,] 113904 --- [o-8443-exec-161] o.s.web.client.RestTemplate              : Reading to [org.springframework.security.oauth2.core.endpoint.OAuth2AccessTokenResponse] as "application/json;charset=UTF-8"
2021-08-20 17:26:08.160 DEBUG [dashboard,,,] 113904 --- [o-8443-exec-161] o.s.web.client.RestTemplate              : HTTP GET https://domain/oauth2/jwk/jwtotp
2021-08-20 17:26:08.162 DEBUG [dashboard,,,] 113904 --- [o-8443-exec-161] o.s.web.client.RestTemplate              : Accept=[text/plain, application/json, application/*+json, */*]
2021-08-20 17:26:08.191 DEBUG [dashboard,,,] 113904 --- [o-8443-exec-161] o.s.web.client.RestTemplate              : Response 200 OK
2021-08-20 17:26:08.191 DEBUG [dashboard,,,] 113904 --- [o-8443-exec-161] o.s.web.client.RestTemplate              : Reading to [java.lang.String] as "text/html;charset=UTF-8"
2021-08-20 17:26:08.215 DEBUG [dashboard,,,] 113904 --- [o-8443-exec-161] .s.o.c.w.OAuth2LoginAuthenticationFilter : Authentication request failed: org.springframework.security.oauth2.core.OAuth2AuthenticationException: [invalid_id_token] An error occurred while attempting to decode the Jwt: Malformed Jwk set

org.springframework.security.oauth2.core.OAuth2AuthenticationException: [invalid_id_token] An error occurred while attempting to decode the Jwt: Malformed Jwk set
    at org.springframework.security.oauth2.client.oidc.authentication.OidcAuthorizationCodeAuthenticationProvider.createOidcToken(OidcAuthorizationCodeAuthenticationProvider.java:226) ~[spring-security-oauth2-client-5.3.4.RELEASE.jar:5.3.4.RELEASE]
    at org.springframework.security.oauth2.client.oidc.authentication.OidcAuthorizationCodeAuthenticationProvider.authenticate(OidcAuthorizationCodeAuthenticationProvider.java:155) ~[spring-security-oauth2-client-5.3.4.RELEASE.jar:5.3.4.RELEASE]
    at org.springframework.security.authentication.ProviderManager.authenticate(ProviderManager.java:199) ~[spring-security-core-5.3.4.RELEASE.jar:5.3.4.RELEASE]
    at org.springframework.security.oauth2.client.web.OAuth2LoginAuthenticationFilter.attemptAuthentication(OAuth2LoginAuthenticationFilter.java:185) ~[spring-security-oauth2-client-5.3.4.RELEASE.jar:5.3.4.RELEASE]
    at org.springframework.security.web.authentication.AbstractAuthenticationProcessingFilter.doFilter(AbstractAuthenticationProcessingFilter.java:212) ~[spring-security-web-5.3.4.RELEASE.jar:5.3.4.RELEASE]
    at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:334) [spring-security-web-5.3.4.RELEASE.jar:5.3.4.RELEASE]
    at org.springframework.security.oauth2.client.web.OAuth2AuthorizationRequestRedirectFilter.doFilterInternal(OAuth2AuthorizationRequestRedirectFilter.java:160) [spring-security-oauth2-client-5.3.4.RELEASE.jar:5.3.4.RELEASE]
    at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:119) [spring-web-5.2.8.RELEASE.jar:5.2.8.RELEASE]
    at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:334) [spring-security-web-5.3.4.RELEASE.jar:5.3.4.RELEASE]
    at org.springframework.security.web.authentication.logout.LogoutFilter.doFilter(LogoutFilter.java:116) [spring-security-web-5.3.4.RELEASE.jar:5.3.4.RELEASE]
    at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:334) [spring-security-web-5.3.4.RELEASE.jar:5.3.4.RELEASE]
    at org.springframework.web.filter.CorsFilter.doFilterInternal(CorsFilter.java:92) [spring-web-5.2.8.RELEASE.jar:5.2.8.RELEASE]
    at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:119) [spring-web-5.2.8.RELEASE.jar:5.2.8.RELEASE]
    at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:334) [spring-security-web-5.3.4.RELEASE.jar:5.3.4.RELEASE]
    at org.springframework.security.web.header.HeaderWriterFilter.doHeadersAfter(HeaderWriterFilter.java:92) [spring-security-web-5.3.4.RELEASE.jar:5.3.4.RELEASE]
    at org.springframework.security.web.header.HeaderWriterFilter.doFilterInternal(HeaderWriterFilter.java:77) [spring-security-web-5.3.4.RELEASE.jar:5.3.4.RELEASE]
    at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:119) [spring-web-5.2.8.RELEASE.jar:5.2.8.RELEASE]
    at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:334) [spring-security-web-5.3.4.RELEASE.jar:5.3.4.RELEASE]
    at org.springframework.security.web.context.SecurityContextPersistenceFilter.doFilter(SecurityContextPersistenceFilter.java:105) [spring-security-web-5.3.4.RELEASE.jar:5.3.4.RELEASE]
    at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:334) [spring-security-web-5.3.4.RELEASE.jar:5.3.4.RELEASE]
    at org.springframework.security.web.context.request.async.WebAsyncManagerIntegrationFilter.doFilterInternal(WebAsyncManagerIntegrationFilter.java:56) [spring-security-web-5.3.4.RELEASE.jar:5.3.4.RELEASE]
    at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:119) [spring-web-5.2.8.RELEASE.jar:5.2.8.RELEASE]
    at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:334) [spring-security-web-5.3.4.RELEASE.jar:5.3.4.RELEASE]
    at org.springframework.security.web.FilterChainProxy.doFilterInternal(FilterChainProxy.java:215) [spring-security-web-5.3.4.RELEASE.jar:5.3.4.RELEASE]
    at org.springframework.security.web.FilterChainProxy.doFilter(FilterChainProxy.java:178) [spring-security-web-5.3.4.RELEASE.jar:5.3.4.RELEASE]
    at org.springframework.web.filter.DelegatingFilterProxy.invokeDelegate(DelegatingFilterProxy.java:358) [spring-web-5.2.8.RELEASE.jar:5.2.8.RELEASE]
    at org.springframework.web.filter.DelegatingFilterProxy.doFilter(DelegatingFilterProxy.java:271) [spring-web-5.2.8.RELEASE.jar:5.2.8.RELEASE]
    at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193) [catalina.jar:9.0.29]
    at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166) [catalina.jar:9.0.29]
    at org.springframework.web.filter.RequestContextFilter.doFilterInternal(RequestContextFilter.java:100) [spring-web-5.2.8.RELEASE.jar:5.2.8.RELEASE]
    at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:119) [spring-web-5.2.8.RELEASE.jar:5.2.8.RELEASE]
    at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193) [catalina.jar:9.0.29]
    at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166) [catalina.jar:9.0.29]
    at org.springframework.web.filter.FormContentFilter.doFilterInternal(FormContentFilter.java:93) [spring-web-5.2.8.RELEASE.jar:5.2.8.RELEASE]
    at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:119) [spring-web-5.2.8.RELEASE.jar:5.2.8.RELEASE]
    at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193) [catalina.jar:9.0.29]
    at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166) [catalina.jar:9.0.29]
    at org.springframework.web.filter.CharacterEncodingFilter.doFilterInternal(CharacterEncodingFilter.java:201) [spring-web-5.2.8.RELEASE.jar:5.2.8.RELEASE]
    at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:119) [spring-web-5.2.8.RELEASE.jar:5.2.8.RELEASE]
    at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193) [catalina.jar:9.0.29]
    at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166) [catalina.jar:9.0.29]
    at com.sso.oidc.config.CORSFilter.doFilter(CORSFilter.java:37) [classes/:0.0.1-SNAPSHOT]
    at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193) [catalina.jar:9.0.29]
    at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166) [catalina.jar:9.0.29]
    at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:202) [catalina.jar:9.0.29]
    at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:96) [catalina.jar:9.0.29]
    at org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:526) [catalina.jar:9.0.29]
    at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:139) [catalina.jar:9.0.29]
    at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:92) [catalina.jar:9.0.29]
    at org.apache.catalina.valves.rewrite.RewriteValve.invoke(RewriteValve.java:555) [catalina.jar:9.0.29]
    at org.apache.catalina.valves.AbstractAccessLogValve.invoke(AbstractAccessLogValve.java:678) [catalina.jar:9.0.29]
    at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:74) [catalina.jar:9.0.29]
    at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:343) [catalina.jar:9.0.29]
    at org.apache.coyote.http11.Http11Processor.service(Http11Processor.java:367) [tomcat-coyote.jar:9.0.29]
    at org.apache.coyote.AbstractProcessorLight.process(AbstractProcessorLight.java:65) [tomcat-coyote.jar:9.0.29]
    at org.apache.coyote.AbstractProtocol$ConnectionHandler.process(AbstractProtocol.java:860) [tomcat-coyote.jar:9.0.29]
    at org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.doRun(NioEndpoint.java:1591) [tomcat-coyote.jar:9.0.29]
    at org.apache.tomcat.util.net.SocketProcessorBase.run(SocketProcessorBase.java:49) [tomcat-coyote.jar:9.0.29]
    at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149) [na:1.8.0_201]
    at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624) [na:1.8.0_201]
    at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61) [tomcat-util.jar:9.0.29]
    at java.lang.Thread.run(Thread.java:748) [na:1.8.0_201]
Caused by: org.springframework.security.oauth2.jwt.JwtException: An error occurred while attempting to decode the Jwt: Malformed Jwk set
    at org.springframework.security.oauth2.jwt.NimbusJwtDecoder.createJwt(NimbusJwtDecoder.java:155) ~[spring-security-oauth2-jose-5.3.4.RELEASE.jar:5.3.4.RELEASE]
    at org.springframework.security.oauth2.jwt.NimbusJwtDecoder.decode(NimbusJwtDecoder.java:129) ~[spring-security-oauth2-jose-5.3.4.RELEASE.jar:5.3.4.RELEASE]
    at org.springframework.security.oauth2.client.oidc.authentication.OidcAuthorizationCodeAuthenticationProvider.createOidcToken(OidcAuthorizationCodeAuthenticationProvider.java:223) ~[spring-security-oauth2-client-5.3.4.RELEASE.jar:5.3.4.RELEASE]
    ... 61 common frames omitted

我不确定为什么会发生此错误,spring 应该使用通过 jwk-set-uri 接收的密钥在内部解码 JWT 令牌。 日志没有提供太多信息。

pom.xml

    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
    </dependency>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-security</artifactId>
    </dependency>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-oauth2-client</artifactId>
    </dependency>

安全配置

@Configuration
@EnableWebSecurity
@Slf4j
//@Import(TrustStoreConfig.class)
public class SecurityConfig extends WebSecurityConfigurerAdapter 

    final Logger logger = LoggerFactory.getLogger(SecurityConfig.class);

    @Override
    protected void configure(HttpSecurity http) throws Exception 

        http.cors().and().csrf().disable().authorizeRequests()
                .antMatchers("/oauth2/**", "/login/**").permitAll().anyRequest().authenticated()
                .and().oauth2Login().authorizationEndpoint()
                .authorizationRequestRepository(new InMemoryRequestRepository()).and()
                .successHandler(this::successHandler);

    

application.yml

logging: 
  level: 
    org.springframework.security.oauth2: DEBUG
    org.springframework.web: TRACE
server: 
  port: 8443
spring: 
  security: 
    oauth2: 
      client: 
        provider: 
          abc: 
            authorization-uri: https://domain/oauth2/authorize
            token-uri: https://domain/oauth2/token
            user-info-uri: https://domain/oauth2/userinfo
            user-name-attribute: sub
            jwk-set-uri: https://domain/oauth2/jwk/jwtotp
        registration: 
          abc: 
            authorization-grant-type: authorization_code
            client-id: ****
            client-secret: ***
            scope: openid,email
            redirect-uri: https://baseUrl/login/oauth2/code/abc

这就是我的 Spring 配置的样子。我找不到太多关于为什么会发生这种情况以及如何解决它的信息。在this 中,一切似乎都由 Spring 在内部管理,并且没有这样的机制来解码 JWT。 请指导。

【问题讨论】:

这是 Nimbus 在无法解析来自授权服务器的 JWK Set 响应时返回的错误。我建议点击 https://domain/oauth2/jwk/jwtotp 端点来看看你得到了什么。另外,我打开了github.com/spring-projects/spring-security/issues/10222,希望为错误添加更多细节。 我确实尝试点击 https://domain/oauth2/jwk/jwtotp 端点并返回一组密钥。 "keys": [ "kty": "RSA", "kid": "****", "use": "sig", "n": "************************************", "e": "AQAB" , "kty": "RSA", "kid": "****", "use": "sig", "n": "******************************************", "e": "AQAB" ,...] 请不要在 cmets 中编写代码,因为您可以看到它完全不可读。改为更新您的问题 您可以考虑使用 Nimbus 的 API 来解析测试中的 JWK 集。 RemoteJWKSet 是 Spring Security 使用的。 【参考方案1】:

我认为您在设置“授权 uri”时遇到了错误。看一看。我相信正确的应该是; 我希望我能帮上忙。

 ...
            .antMatchers("/oauth2/authorize**", "/oauth2/**" ..

【讨论】:

输入错误,实际上是“/oauth2/authorize”,并且授权 uri 工作得很好,应用程序确实被重定向到登录页面,并且它在此之后处理 /token,你可以在日志中查看。

以上是关于Spring Boot OAuth2/OpenID Connect Client 尝试解码 Jwt: Malformed Jwk set 时出错的主要内容,如果未能解决你的问题,请参考以下文章

Oauth2/Openid 连接。如何撤销未知的访问/刷新令牌

Keycloak 的 OAuth2 / OpenID Connect 端点是啥?

使用 OAuth2/OpenId Connect 和微服务进行身份验证和授权

OAuth2/OpenID Connect 保护 API 的自动化 API 测试

OIDC-基于OAuth2的下一代身份认证授权协议

Spring Boot 学习例子