求SpringBoot集成MockPass(SingPass v2-NDI OIDC)示例及JWT解码方案
SpringBoot集成MockPass(SingPass v2 - NDI OIDC)问题解决与示例说明
问题原因
你遇到的invalid_id_token错误,核心原因是MockPass返回的ID Token采用JWE加密格式,而Spring Security OAuth2默认仅处理JWS签名格式的Token,因此会触发JWEHeader转JWSHeader的类型转换异常。
自定义Token解码方案
要解决这个问题,你需要自定义JWT解码器,让它同时支持JWE解密和JWS验证,同时可以在Token流转过程中拦截并自行解码。
1. 配置支持JWE的JwtDecoder
以下是完整的Spring Security配置类,包含自定义解码器和Token拦截逻辑:
@Configuration public class MockPassSecurityConfig { @Value("${spring.security.oauth2.client.registration.mockpass.client-id}") private String clientId; @Value("${spring.security.oauth2.client.provider.mockpass.jwk-set-uri}") private String jwkSetUri; @Bean public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception { http .authorizeHttpRequests(auth -> auth.anyRequest().authenticated()) .oauth2Login(oauth2 -> oauth2 .tokenEndpoint(token -> token.accessTokenResponseClient(customTokenResponseClient())) .userInfoEndpoint(userInfo -> userInfo.oidcUserService(customOidcUserService())) ); return http.build(); } // 自定义Token响应客户端,用于拦截原始ID Token并自行解码 private OAuth2AccessTokenResponseClient<OAuth2AuthorizationCodeGrantRequest> customTokenResponseClient() { DefaultAuthorizationCodeTokenResponseClient client = new DefaultAuthorizationCodeTokenResponseClient(); client.setAccessTokenResponseConverter(tokenParams -> { // 获取原始ID Token String rawIdToken = (String) tokenParams.get("id_token"); // 自行解码JWE格式的Token try { JWT jwt = JWTParser.parse(rawIdToken); if (jwt instanceof EncryptedJWT encryptedJWT) { // 从JWK Set加载解密密钥 JWKSource<SecurityContext> jwkSource = JWKSet.load(new URL(jwkSetUri)).toJWKSource(); JWEKeySelector<SecurityContext> keySelector = new JWEKeySelector<>(jwkSource); JWK decryptionKey = keySelector.selectJWEKeys(encryptedJWT.getHeader(), null).get(0); // 解密Token encryptedJWT.decrypt(new DirectDecrypter(decryptionKey.toSecretKey())); System.out.println("解密后的Claims: " + encryptedJWT.getJWTClaimsSet()); } } catch (ParseException | JOSEException | MalformedURLException e) { throw new OAuth2AuthenticationException(new OAuth2Error("invalid_id_token", "解码ID Token失败", null)); } return DefaultAuthorizationCodeTokenResponseClient.DEFAULT_CONVERTER.convert(tokenParams); }); return client; } // 自定义OIDC用户服务,基于解密后的Token构建用户信息 private OidcUserService customOidcUserService() { OidcUserService delegate = new OidcUserService(); delegate.setOidcUserMapper(userInfo -> { // 这里可以直接使用解密后的ID Token Claims OidcIdToken idToken = userInfo.getIdToken(); return new DefaultOidcUser( AuthorityUtils.createAuthorityList("ROLE_USER"), idToken, "sub" // 用sub作为用户名标识 ); }); return delegate; } // 配置支持JWE的JwtDecoder @Bean public JwtDecoder jwtDecoder() throws MalformedURLException { JWKSource<SecurityContext> jwkSource = JWKSet.load(new URL(jwkSetUri)).toJWKSource(); ConfigurableJWTProcessor<SecurityContext> jwtProcessor = new DefaultJWTProcessor<>(); // 设置JWE解密密钥选择器 jwtProcessor.setJWEKeySelector(new JWEKeySelector<>(jwkSource)); // 设置JWS验证密钥选择器 jwtProcessor.setJWSKeySelector(JWSAlgorithmFamilyJWSKeySelector.fromJWKSource(jwkSource)); return new NimbusJwtDecoder(jwtProcessor); } }
2. 配置文件示例(application.yml)
spring: security: oauth2: client: registration: mockpass: client-id: YOUR_MOCKPASS_CLIENT_ID client-secret: YOUR_MOCKPASS_CLIENT_SECRET scope: openid,profile authorization-grant-type: authorization_code redirect-uri: "{baseUrl}/login/oauth2/code/mockpass" provider: mockpass: authorization-uri: https://mockpass.singpass.gov.sg/oauth/v2/authorize token-uri: https://mockpass.singpass.gov.sg/oauth/v2/token user-info-uri: https://mockpass.singpass.gov.sg/oauth/v2/userinfo jwk-set-uri: https://mockpass.singpass.gov.sg/oauth/v2/jwks user-name-attribute: sub
示例项目结构
src/ ├── main/ │ ├── java/com/yourcompany/mockpassdemo/ │ │ ├── MockpassDemoApplication.java │ │ └── config/MockPassSecurityConfig.java │ └── resources/ │ └── application.yml
关键注意事项
- 确保你的Spring Boot版本在2.7.x及以上,Spring Security 5.7.x及以上,这些版本对JWE的支持更成熟
- 确认MockPass后台配置的
redirect-uri与Spring Boot应用配置的完全一致 - 调试时可以先将原始ID Token复制到JWT调试工具,但JWE格式需要密钥才能解密,建议用代码中的日志打印解密后的Claims
内容的提问来源于stack exchange,提问作者GLEE
相关产品推荐
相关产品推荐

