带有加密 JWT 访问令牌的 Spring Boot OAuth2
Posted
技术标签:
【中文标题】带有加密 JWT 访问令牌的 Spring Boot OAuth2【英文标题】:Spring Boot OAuth2 with encrypted JWT access token 【发布时间】:2017-02-06 02:52:16 【问题描述】:在我的 Spring Bott 应用程序中,我使用授权/资源服务器配置了自己的 OAuth2。
我已经实现了以下 JwtAccessTokenConverter:
@Bean
public JwtAccessTokenConverter accessTokenConverter()
JwtAccessTokenConverter converter = new JwtAccessTokenConverter()
@Override
public OAuth2AccessToken enhance(OAuth2AccessToken accessToken, OAuth2Authentication authentication)
DBUserDetails user = (DBUserDetails) authentication.getUserAuthentication().getPrincipal();
final Map<String, Object> additionalInfo = new HashMap<>();
additionalInfo.put("user_id", user.getUser().getId());
((DefaultOAuth2AccessToken) accessToken).setAdditionalInformation(additionalInfo);
OAuth2AccessToken enhancedToken = super.enhance(accessToken, authentication);
return enhancedToken;
;
converter.setSigningKey("123");
DefaultAccessTokenConverter accessTokenConverter = new DefaultAccessTokenConverter();
DefaultUserAuthenticationConverter userTokenConverter = new DefaultUserAuthenticationConverter();
userTokenConverter.setUserDetailsService(userDetailsService);
accessTokenConverter.setUserTokenConverter(userTokenConverter);
converter.setAccessTokenConverter(accessTokenConverter);
return converter;
现在我的应用程序生成以下令牌,例如:
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VyX2lkIjoxNzgzNTEsInVzZXJfbmFtZSI6ImFkbWluIiwic2NvcGUiOlsicmVhZCIsIndyaXRlIl0sImV4cCI6MTQ3NTA5NDA4NSwiYXV0aG9yaXRpZXMiOlsiUEVSTUlTU0lPTl9ERUxFVEVfT1dOX0NSSVRFUklPTiIsIlBFUk1JU1NJT05fREVMRVRFX09XTl9DT01NRU5UIiwiUEVSTUlTU0lPTl9VUERBVEVfT1dOX0NSSVRFUklPTiIsIlBFUk1JU1NJT05fVVBEQVRFX0FOWV9DUklURVJJT04iLCJQRVJNSVNTSU9OX0RFTEVURV9PV05fQ1JJVEVSSU9OX0dST1VQIiwiUEVSTUlTU0lPTl9ERUxFVEVfQU5ZX0RFQ0lTSU9OIiwiUEVSTUlTU0lPTl9DUkVBVEVfVk9URSIsIlBFUk1JU1NJT05fREVMRVRFX0FOWV9DUklURVJJT04iLCJQRVJNSVNTSU9OX0NSRUFURV9DUklURVJJT05fR1JPVVAiLCJQRVJNSVNTSU9OX0RFTEVURV9PV05fREVDSVNJT04iLCJQRVJNSVNTSU9OX0NSRUFURV9ERUNJU0lPTiIsIlBFUk1JU1NJT05fREVMRVRFX0FOWV9DUklURVJJT05fR1JPVVAiLCJQRVJNSVNTSU9OX0RFTEVURV9BTllfVk9URSIsIlBFUk1JU1NJT05fREVMRVRFX09XTl9WT1RFIiwiUEVSTUlTU0lPTl9VUERBVEVfT1dOX0NSSVRFUklPTl9HUk9VUCIsIlBFUk1JU1NJT05fVVBEQVRFX0FOWV9DUklURVJJT05fR1JPVVAiLCJQRVJNSVNTSU9OX1VQREFURV9BTllfQ09NTUVOVCIsIlBFUk1JU1NJT05fVVBEQVRFX09XTl9DT01NRU5UIiwiUEVSTUlTU0lPTl9VUERBVEVfQU5ZX0RFQ0lTSU9OIiwiUEVSTUlTU0lPTl9BUFBFTkRfREVDSVNJT04iLCJQRVJNSVNTSU9OX1VQREFURV9PV05fVk9URSIsIlBFUk1JU1NJT05fVVBEQVRFX0FOWV9WT1RFIiwiUEVSTUlTU0lPTl9ERUxFVEVfQU5ZX0NPTU1FTlQiLCJQRVJNSVNTSU9OX1VQREFURV9PV05fREVDSVNJT04iLCJQRVJNSVNTSU9OX0NSRUFURV9DT01NRU5UIiwiUEVSTUlTU0lPTl9DUkVBVEVfQ1JJVEVSSU9OIiwiUEVSTUlTU0lPTl9SRUFEX0FDVFVBVE9SX0RBVEEiXSwianRpIjoiMWU3OGMzMGYtNTY0ZS00NjliLWE1MmMtODlhOGM4YzFiZmY2IiwiY2xpZW50X2lkIjoiZGVjaXNpb253YW50ZWRfY2xpZW50X2lkIn0.Cnj_7b3FAanmL0Y-_kxcH2f4yjLFHOw-4NOVr67WZ88
这个token可以在这里用JWT调试器解码https://jwt.io/
我不想将这个令牌的内部暴露给外部世界,并希望以某种方式对这个令牌进行编码。
如何用 Spring Boot、OAuth2、JWT 实现?
【问题讨论】:
这个 jwt oauth2 spring 安全实现的源代码可能会有所帮助:github.com/absolutegalaber/jwt-oauth2-example 你为什么要对用户隐藏内部信息,他们不是已经知道关于他们自己的一切了吗?如果我看到你的令牌,那么我已经劫持了你的会话(即使内部是编码/加密的)。无论如何结帐 JWE(JSON Web 加密)tools.ietf.org/html/rfc7516 P.S.您是否考虑过屏蔽权限以大幅缩小该令牌? @Alex,感谢您的回答。现在我正在考虑缩小权限。而且,我还创建了另一张关于 JWE 的票 - ***.com/questions/39768669/… @alexanoid 你找到合适的解决方案了吗 老实说,我现在不记得了,因为那是很多年前的事了( 【参考方案1】:我试试这个,它对我有用:https://gist.github.com/salgmachine/352799a6052b02901982dcbf85d30346
创建自定义 JwtAccessTokenConverter
public class JwtJweAccessTokenConverter extends JwtAccessTokenConverter
RSAKey recipientJWK, recipientPublicJWK;
public JwtJweAccessTokenConverter()
try
recipientJWK = new RSAKeyGenerator(2048).keyID("456").keyUse(KeyUse.ENCRYPTION).generate();
recipientPublicJWK = recipientJWK.toPublicJWK();
catch (JOSEException e)
// TODO Auto-generated catch block
e.printStackTrace();
@Override
protected String encode(OAuth2AccessToken accessToken, OAuth2Authentication authentication)
String jwt = super.encode(accessToken, authentication);
try
// jwt is already signed at this point (by JwtAccessTokenConverter)
SignedJWT parsed = SignedJWT.parse(jwt);
// Create JWE object with signed JWT as payload
JWEObject jweObject = new JWEObject(
new JWEHeader.Builder(JWEAlgorithm.RSA_OAEP_256, EncryptionMethod.A256GCM).contentType("JWT") // required
// to
// indicate
// nested
// JWT
.build(),
new Payload(parsed));
// Encrypt with the recipient's public key
jweObject.encrypt(new RSAEncrypter(recipientPublicJWK));
// Serialise to JWE compact form
String jweString = jweObject.serialize();
return jweString;
catch (Exception e)
e.printStackTrace();
return jwt;
@Override
protected Map<String, Object> decode(String token)
try
// basically treat the incoming token as an encrypted JWT
EncryptedJWT parse = EncryptedJWT.parse(token);
// decrypt it
RSADecrypter dec = new RSADecrypter(recipientJWK);
parse.decrypt(dec);
// content of the encrypted token is a signed JWT (signed by
// JwtAccessTokenConverter)
SignedJWT signedJWT = parse.getPayload().toSignedJWT();
// pass on the serialized, signed JWT to JwtAccessTokenConverter
return super.decode(signedJWT.serialize());
catch (ParseException e)
e.printStackTrace();
catch (JOSEException e)
e.printStackTrace();
return super.decode(token);
并配置您的 Oauth2 身份验证服务器和资源以使用您的自定义 JwtAccessTokenConverter
@Bean
public TokenStore tokenStore()
return new JwtTokenStore(accessTokenConverter());
@Bean
public JwtAccessTokenConverter accessTokenConverter()
final JwtAccessTokenConverter converter = new JwtJweAccessTokenConverter();
final KeyStoreKeyFactory keyStoreKeyFactory = new KeyStoreKeyFactory(new ClassPathResource("mytest.jks"),
"mypass".toCharArray());
converter.setKeyPair(keyStoreKeyFactory.getKeyPair("mytest"));
return converter;
检查 github 链接以获取完整的代码示例
【讨论】:
以上是关于带有加密 JWT 访问令牌的 Spring Boot OAuth2的主要内容,如果未能解决你的问题,请参考以下文章
使用 Spring Security OAuth2 进行访问令牌请求的 JWT 不记名交换