更新到 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();
}
我已经解决了我的问题。问题出在我的 securityFilterChain 中,我没有在这里发布。我在我的链中使用“oauth2ResourceServer”两次用于两条不同的路线。当我删除一个后,之前不起作用的第一个开始工作。