OAuth 2.0 资源服务器 JWT,无效签名

问题描述 投票:0回答:1

更新到 Spring Boot 3.1.8(尤其是 Spring-Security 6.x)和 Java 21 后,我从 JWT 收到此错误。 我的客户的

Www-Authenticate
标头包含以下错误消息:

Bearer error="invalid_token", error_description="An error occurred while attempting to decode the Jwt: Signed JWT rejected: Invalid signature"

我的后端错误是:

ERROR: 'invalid_token', DESCRIPTION 'true', URI 'https://tools.ietf.org/html/rfc6750#section-3.1'

我的堆栈跟踪是:

cause: 'org.springframework.security.oauth2.jwt.BadJwtException: An error occurred while attempting to decode the Jwt: Signed JWT rejected: Invalid signature',
 stackTrace [org.springframework.security.oauth2.server.resource.authentication.JwtAuthenticationProvider.getJwt(JwtAuthenticationProvider.java:103),
 org.springframework.security.oauth2.server.resource.authentication.JwtAuthenticationProvider.authenticate(JwtAuthenticationProvider.java:88),
 org.springframework.security.authentication.ProviderManager.authenticate(ProviderManager.java:182),
 org.springframework.security.authentication.ObservationAuthenticationManager.lambda$authenticate$1(ObservationAuthenticationManager.java:54),
 io.micrometer.observation.Observation.lambda$observe$4(Observation.java:544),
 io.micrometer.observation.Observation.observeWithContext(Observation.java:603),
 io.micrometer.observation.Observation.observe(Observation.java:544),
 org.springframework.security.authentication.ObservationAuthenticationManager.authenticate(ObservationAuthenticationManager.java:53),

我的应用程序属性如下所示:

auth:
  jwt:
    jwk-set-uri: http://some.service:9000
    claims:
      aud: "MY_CLOUD"
    publicKey:
    signingKey:
    selfSigned: false

在更新到 Spring Boot 3.1.8(

SecurityFilterChain
等)和 Java 21 之前,我使用此库来签署我的 jwt

implementation group: 'com.nimbusds', name: 'nimbus-jose-jwt', version: '9.24.4+'

和带有 Spring Boot 2.7.18 的 Java 11

我的 JWT 令牌的创建和签名如下所示:

private String myToken() {
   
    Calendar calendar = Calendar.getInstance();
    JWTClaimsSet claims = new JWTClaimsSet.Builder()
            .claim("sub_id", "xyz")
            .claim("groups", "myGroup")
            .issueTime(calendar.getTime())
            .expirationTime(DateUtils.addMinutes(calendar.getTime(),
                    30))
            .build();
    Payload payload = new Payload(claims.toJSONObject());
    JWSHeader header = new JWSHeader(JWSAlgorithm.RS256);
    JWSObject jwsObject = new JWSObject(header, payload);
    try {
        com.nimbusds.jose.JWSSigner jwsSigner = getJWSSigner();
        jwsObject.sign(jwsSigner);
    } catch (JOSEException | NoSuchAlgorithmException | InvalidKeySpecException e) {
        throw new BadCredentialsException("Fehler beim Signieren des JWTs aufgetreten", e);
    }
    return jwsObject.serialize();
}


private com.nimbusds.jose.JWSSigner getJWSSigner() throws KeyLengthException, NoSuchAlgorithmException, InvalidKeySpecException {
    //variable 'signingKey will be injected from outside into container
    PKCS8EncodedKeySpec keySpec = new PKCS8EncodedKeySpec(Base64.getDecoder().decode(signingKey.getBytes()));
    java.security.KeyFactory keyFactory = KeyFactory.getInstance("RSA");
    java.security.PrivateKey privateKey = keyFactory.generatePrivate(keySpec);
    return new RSASSASigner(privateKey);
}

我的 JWTDecoder 看起来像这样:

JwtDecoder publicKeyDecoder() {
    NimbusJwtDecoder jwtDecoder = null;      
    KeyFactory keyFactory = KeyFactory.getInstance("RSA");
    X509EncodedKeySpec spec = new X509EncodedKeySpec(Base64.getDecoder().decode(publicKey.getBytes()));
    RSAPublicKey rsaPublicKey = (RSAPublicKey) keyFactory.generatePublic(spec);
    jwtDecoder = NimbusJwtDecoder.withPublicKey(rsaPublicKey).build();
    jwtDecoder.setJwtValidator(getTokenValidator());
    return jwtDecoder;
}

根据此错误,我的方法

getJWSSigner()
在 spring-boot 3.1.8 上无法正常工作 因为到目前为止我明白,我的签名将在这个方法中创建
getJWSSigner()
,它将在上面
jwsObject.sign(jwsSigner)
签名。但也许这是错误的假设。

经过多次尝试并将

nimbus-jose-jwt
更新到版本
9.37.3
我仍然遇到同样的错误。

也在

jwt.io
上我收到此消息:
Invalid Signature

第一部分header和第二部分payload将被正确解码,但第三部分无效。 如果我在 Spring Boot 2.7.8、Java 11 和

Nimbusds-Jose 9.24.4+
上使用相同的代码和相同的键值对,它就可以正常工作。

解决方案

这是我的安全过滤器链

@Bean
public SecurityFilterChain configure(HttpSecurity http) throws Exception {
    http
            .authorizeHttpRequests(authorizeRequests ->
                    authorizeRequests.requestMatchers(HttpMethod.POST,".*(/user)").permitAll())
            .oauth2ResourceServer(oauth2ResourceServer ->
                    oauth2ResourceServer
                            .jwt(jwt ->
                                    jwt.jwtAuthenticationConverter(new MyAuthenticatonConverter())
                                            .decoder(createJWTDecoderX()))
            )
            .authorizeHttpRequests(authorizeRequests ->
                    authorizeRequests.requestMatchers(HttpMethod.POST,".*(/aticle)").permitAll())
            .oauth2ResourceServer(oauth2ResourceServer ->
                    oauth2ResourceServer
                            .jwt(jwt ->
                                    jwt.jwtAuthenticationConverter(new MyAuthenticatonConverter())
                                            .decoder(createJWTDecoderY())
            );
    return http.build();
}

JWTAuthenticationprovier 有一个构造函数。 每次定义 JWTDecoder 时,都会调用此构造函数。根据这个链,我应该有两个不同的身份验证提供者和两个不同的 JWTDecoder。 但当我删除其中一个后,另一个就开始工作了。 AutheticationProvider 的这种行为似乎就像单例:

我的解决方案代码

@Bean
public SecurityFilterChain configure(HttpSecurity http) throws Exception {
    http
            .authorizeHttpRequests(authorizeRequests ->
                    authorizeRequests.requestMatchers(HttpMethod.POST,".*(/user)").permitAll())
            .oauth2ResourceServer(oauth2ResourceServer ->
                    oauth2ResourceServer
                            .jwt(jwt ->
                                    jwt.jwtAuthenticationConverter(new MyAuthenticatonConverter())
                                            .decoder(createJWTDecoderX()))
            ));
    return http.build();
}
java spring-boot jwt nimbus-jose-jwt
1个回答
0
投票

我已经解决了我的问题。问题出在我的 securityFilterChain 中,我没有在这里发布。我在我的链中使用“oauth2ResourceServer”两次用于两条不同的路线。当我删除一个后,之前不起作用的第一个开始工作。

© www.soinside.com 2019 - 2024. All rights reserved.