通过refresh token刷新access token时,refresh token也随之更新的问题?
项目背景:项目中使用SpringBoot集成OAuth2.0,实现对token的管理,此处包含两种token类型,refresh token和access token,两者都具有有效期,在OAuth的设计模式中往往refresh token的有效期都比较长(一般设置7天),而access token的有效期相对较短(一般为数个小时)。所以在具体的实施过程中,当access token过期而refresh token有效的时候,使用后者重新获取新的access token。
问题描述:最近在使用UAA开源项目时,发现每次使用refresh token刷新access token的时候,不光access token更新了,refresh token 同样也更新了,深究了UAA和OAuth的源码发现出现这种现象的原因由以下两点。
在UAA开源项目中,refresh token有两者使用模式:重复使用和非重复使用。
所谓重复使用指的是登陆后初次生成的refresh token一直保持不变,直到过期;
非重复使用指的是在每一次使用refresh token刷新access token的过程中,refresh token也随之更新,即生成新的refresh token。在UAA项目中这一设置是通过UaaConfiguration类reuseRefreshTokens(boolean b)(默认为true)来设置完成;
下面是代码重现的结果分析:首先分析refresh token重复使用的情况,即在UaaConfiguration设置如下:
包名:com.yourcompany.uaa.config,类名:UaaConfiguration类
@Override
public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
Collection tokenEnhancers = applicationContext.getBeansOfType(TokenEnhancer.class).values();
TokenEnhancerChain tokenEnhancerChain = new TokenEnhancerChain();
tokenEnhancerChain.setTokenEnhancers(new ArrayList<>(tokenEnhancers));
endpoints
.authenticationManager(authenticationManager)
.tokenStore(tokenStore())
.tokenEnhancer(tokenEnhancerChain)
//该字段设置设置refresh token是否重复使用,true:reuse;false:no reuse.
.reuseRefreshTokens(true);
}
用户登陆获取:同时获取access token 和 refresh token
access token:
access token: eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VyX25hbWUiOiJhZG1pbiIsInNjb3BlIjpbIm9wZW5pZCJdLCJleHAiOjE1MzkwMDEwNTksImlhdCI6MTUzOTAwMDk2OSwiYXV0aG9yaXRpZXMiOlsiUk9MRV9BRE1JTiIsIlJPTEVfVVNFUiJdLCJqdGkiOiI2YTY3YTQzNC0xNTk2LTQ3YTYtYTNmNS0zZmNjZTljOTJkMjQiLCJjbGllbnRfaWQiOiJ3ZWJfYXBwIn0.ZRSqW7aq3Zph04IuBKGlpyLBeMMf_vZZn8Ac50mJeUZpeWLYr_Mz3cXd0Zlp2DHMRZcpheZQhuqWCa4AaWkTOPuIaeVH-Gpooh_lwO3LPA93sqb4jwdBUoD0A62C4roo71Sz50Fc2WNo0h9t_XSO2L-tS6zFWoxLhqYkVhR_HpGTW_6h0VWixgik8W0lsU4h3oYLfI8G1cdAELtBHYB5n9YllDhEp4_ZXp1npr9UIsYKyJxXOAFBp_j1SBtoqu7fikA2zkIdVh54-EQRZozaII-LZwRpt5sLyCgSrs8eMMdW6ow_TSMyYWCovl5eaRZEv6-VW9XFH7QtORk1W-VjSw
使用jwt解析之后的对应的明文为:
{
"user_name": "admin",
"scope": [
"openid"
],
"exp": 1539001059,
"iat": 1539000969,
"authorities": [
"ROLE_ADMIN",
"ROLE_USER"
],
"jti": "6a67a434-1596-47a6-a3f5-3fcce9c92d24",
"client_id": "web_app"
}
refresh token:
refresh token: eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VyX25hbWUiOiJhZG1pbiIsInNjb3BlIjpbIm9wZW5pZCJdLCJhdGkiOiI2YTY3YTQzNC0xNTk2LTQ3YTYtYTNmNS0zZmNjZTljOTJkMjQiLCJleHAiOjE1MzkwMDQ1NjksImlhdCI6MTUzOTAwMDk2OSwiYXV0aG9yaXRpZXMiOlsiUk9MRV9BRE1JTiIsIlJPTEVfVVNFUiJdLCJqdGkiOiIxOTNhN2MyZC00Y2YxLTRhZjctODExOS0xZTljOWY5MTZiNmQiLCJjbGllbnRfaWQiOiJ3ZWJfYXBwIn0.XQsgqTkXqrS1X3u21tQqaHIvDZINcAtZ475kYSNikF7mFj8F91pyt9M6YRh9D_l0xmGqRPHldlwfMrxdQhSEir8WdrtfSOar9QpT4fZv9tyn9xPdKG5uq0jrtz2xq5ws17ZL-9PCFZJJnLQ6tLBXJ-hQnGEhA0Jq1JtCfpRIQ3VJM5561iN07yerjcIDfNLKQAGLd7I6Ilw_qRHDGeP0iZg5S-KPL-MApNwDfsxnHinRxeKUC2o2x0pJ8bhFKJQLIa-G4zKYrMhUc9bhzqMSERX3eJvsQldlr_6AoQgPokRTu0VHRa4Z7qOxTUC8hz2VDtxqOvzmQhrzeIyaSoLq1w
使用jwt解析之后的对应的明文为:
{
"user_name": "admin",
"scope": [
"openid"
],
"ati": "6a67a434-1596-47a6-a3f5-3fcce9c92d24",
"exp": 1539004569,
"iat": 1539000969,
"authorities": [
"ROLE_ADMIN",
"ROLE_USER"
],
"jti": "193a7c2d-4cf1-4af7-8119-1e9c9f916b6d",
"client_id": "web_app"
}
待access token过期之后,执行刷新操作,重新获取如下access token 和 refresh token:
access token
access token: eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VyX25hbWUiOiJhZG1pbiIsInNjb3BlIjpbIm9wZW5pZCJdLCJleHAiOjE1MzkwMDExNTEsImlhdCI6MTUzOTAwMTA2MSwiYXV0aG9yaXRpZXMiOlsiUk9MRV9BRE1JTiIsIlJPTEVfVVNFUiJdLCJqdGkiOiI4ZDJkNjNhZS0wZmYwLTQ5OWEtODBhMy04ZTVhMTA0MTBmODMiLCJjbGllbnRfaWQiOiJ3ZWJfYXBwIn0.RFkfY_ghMt9Qh54HMjGWjzpJN1bRhsyMWiHlXPQ6vS1_KoKXyRD-Wvagqnd-cW_rH6ZuCqafGetJC5EcX7EClvAonP8o4vgK9CkidS68QBSbgm_yNqaBZDhbtq9sI0qMGDJ1U-YKjUtXOljIA7pxzdb0nqWJgYUanC4_X3snMUABpJTDBWouAUnk0L_V0gcSnX_qnaBxAg9hYG80T6F583tlCJDKawLGqZXdTpAm57ymoQKkkhXYvSvwKQ6DNcuJgZQSzpd5aAWvR_Sqs_BbqbhXlOZjjDO0caO5LqWGPLKfjBY8lFYrclr1-WG9WnDcOB5IlYKxZe27-sLE2yKBdw
解析之后明文:
{
"user_name": "admin",
"scope": [
"openid"
],
"exp": 1539001151,
"iat": 1539001061,
"authorities": [
"ROLE_ADMIN",
"ROLE_USER"
],
"jti": "8d2d63ae-0ff0-499a-80a3-8e5a10410f83",
"client_id": "web_app"
}
refrsh token:
refresh token:
eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VyX25hbWUiOiJhZG1pbiIsInNjb3BlIjpbIm9wZW5pZCJdLCJhdGkiOiI4ZDJkNjNhZS0wZmYwLTQ5OWEtODBhMy04ZTVhMTA0MTBmODMiLCJleHAiOjE1MzkwMDQ1NjksImlhdCI6MTUzOTAwMTA2MSwiYXV0aG9yaXRpZXMiOlsiUk9MRV9BRE1JTiIsIlJPTEVfVVNFUiJdLCJqdGkiOiIxOTNhN2MyZC00Y2YxLTRhZjctODExOS0xZTljOWY5MTZiNmQiLCJjbGllbnRfaWQiOiJ3ZWJfYXBwIn0.Mpw38-_isKhsXehUo7Fm3X3Sm6QvohIuq40ArTmfGkk2CEGNs7hyopJf17jjs2d3rCjNeiIcQqQKZPdIus0jrMv1X-dcxXxr_JFm0GgmVR0x-E2Gnva0XsNR2zVX8UddN885UCySCcVLuqdeMjxExf8ALzJNEtJvMiE11zug9FTSuXEZQCfbszHlVeoIVPLGQMK34c6XlqEbGLi9P4Z4QD5bptu5KU6f6Gf0ScoFISEXLBoAj7XKFQG86OjDamBJGZXgNw5R6RPU5qFS7SE_pfxenl_vCmcjvrTEfqsA98rCEGAfVCZyoF3Oj3S2uSkpQTaHo2UHLGY6Lr5ejwpbZw
解析之后明文:
{
"user_name": "admin",
"scope": [
"openid"
],
"ati": "8d2d63ae-0ff0-499a-80a3-8e5a10410f83",
"exp": 1539004569,
"iat": 1539001061,
"authorities": [
"ROLE_ADMIN",
"ROLE_USER"
],
"jti": "193a7c2d-4cf1-4af7-8119-1e9c9f916b6d",
"client_id": "web_app"
}
结果分析:
通过对比refresh token刷新前后的明文信息可知,两个refresh token的jti(jwt-token-id,token的唯一属性)都为193a7c2d-4cf1-4af7-8119-1e9c9f916b6d,说明两个refresh token是同意个token,并为更新。心细的读者可能发现两个refresh token的加密字符串是不同的,其实这个原因是由于refresh token 中有个ati字段(access-token-id)信息,该字段是当前access token的唯一Id,由于access token更新了(由刷新前后access token的jti不同可证明),故而refresh token也相应作出改变,但是其过期时间等关键信息并未改变。这个也可以从下面的关键代码很明显的看出。
这种模式下的refresh token的过期仍然是以初次生成的时间为准。
- 现在介绍refresh token 非重复利用的场景:
非重复使用: 即当access token 更新的时候,同时更新refresh token,这样就把refresh token的过期时间一直往后延续。该模式的用意就是通过更新refresh token实现永远不需要再次登陆的目的。除非前后两次对项目的操作时间间隔超出了refresh token的有效时间段。
下面看一下演示效果,同样设置UaaConfiguration,
@Override
public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
Collection tokenEnhancers = applicationContext.getBeansOfType(TokenEnhancer.class).values();
TokenEnhancerChain tokenEnhancerChain = new TokenEnhancerChain();
tokenEnhancerChain.setTokenEnhancers(new ArrayList<>(tokenEnhancers));
endpoints
.authenticationManager(authenticationManager)
.tokenStore(tokenStore())
.tokenEnhancer(tokenEnhancerChain)
//该字段设置设置refresh token是否重复使用,true:reuse;false:no reuse.
.reuseRefreshTokens(false);
}
access token:
eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VyX25hbWUiOiJhZG1pbiIsInNjb3BlIjpbIm9wZW5pZCJdLCJleHAiOjE1MzkwMDEzMzQsImlhdCI6MTUzOTAwMTI0NCwiYXV0aG9yaXRpZXMiOlsiUk9MRV9BRE1JTiIsIlJPTEVfVVNFUiJdLCJqdGkiOiI3NzM5NjdhZS01ODBkLTQzNTItYjQ1OS1jY2I5NWQyZWQyMGQiLCJjbGllbnRfaWQiOiJ3ZWJfYXBwIn0.Tag4FBZS2cj9RBnZaYkw3CbWD-3c-xDabMsf-j7fxr_p5LKDrMyq2rVUmdt_htQoHgDuawcot_m4z6FX6_1kk-592Y-rcxFPuRZ4Fma7kOLbUP8JSOWydNxIH0pPZsiFpKjUTICrFrYPQz_l_2xiPSl8iHBEGBWWYHYSFCu8krwP9pYTwJCXNaQ8fEpR9Gz4PouejWUwi4gOPPZoxZBnw_9YFd27Am2vOesghMphQHOIy65ClrGXLPxLe3hRg5WggPtKm1ekJmr5OyYH3V4NRowYQ8JeaPDA7peOcSK5IfrDcTqZS5veacR7elw0cjq0ykcWjyCDxbYqLE5INNjReg
解析后为:
{
"user_name": "admin",
"scope": [
"openid"
],
"exp": 1539001334,
"iat": 1539001244,
"authorities": [
"ROLE_ADMIN",
"ROLE_USER"
],
"jti": "773967ae-580d-4352-b459-ccb95d2ed20d",
"client_id": "web_app"
}
refresh token:
eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VyX25hbWUiOiJhZG1pbiIsInNjb3BlIjpbIm9wZW5pZCJdLCJhdGkiOiI3NzM5NjdhZS01ODBkLTQzNTItYjQ1OS1jY2I5NWQyZWQyMGQiLCJleHAiOjE1MzkwMDQ4NDQsImlhdCI6MTUzOTAwMTI0NCwiYXV0aG9yaXRpZXMiOlsiUk9MRV9BRE1JTiIsIlJPTEVfVVNFUiJdLCJqdGkiOiIzMjZiMWRhNS0xYzc4LTQ5NTQtYTVmNC1lMDNlMTdmOTk5ZjMiLCJjbGllbnRfaWQiOiJ3ZWJfYXBwIn0.ScQBtEUMrKJKtFIZMuwkRGNyzRuZCkMJhHu1etwIEepUQ6RqB1EWejFRVRJGZoycMDKeNkMbB46dwOOP47NxQu5ITDBs5ccx7_7WlMD8z2JDVTpr9kRIXQBFMBHVH6VuZEmxIm2IGWn-qVWwLVUzP4xRiDEj8svXJD_J4gY4Yxrr1sT9C8NTFCN45EvfC2ELcR7tBSRs1iHLxdQCvKaEhmk22f006CbgKZp3CUj5nTV--8hIamF7xiNJ_P2p0jdURf5PMg_KDznEZsHnRRucfvQpT0jrxheGbngupVRqC5laTYmdGEac7J-eo_ONmKuxKoP_BgOChCY_IGhExX__zw
解析后为:
{
"user_name": "admin",
"scope": [
"openid"
],
"ati": "773967ae-580d-4352-b459-ccb95d2ed20d",
"exp": 1539004844,
"iat": 1539001244,
"authorities": [
"ROLE_ADMIN",
"ROLE_USER"
],
"jti": "326b1da5-1c78-4954-a5f4-e03e17f999f3",
"client_id": "web_app"
}
待access token过期之后,执行刷新操作,重新获取如下access token 和 refresh token:
access token:
eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VyX25hbWUiOiJhZG1pbiIsInNjb3BlIjpbIm9wZW5pZCJdLCJleHAiOjE1MzkwMDE0MzYsImlhdCI6MTUzOTAwMTM0NiwiYXV0aG9yaXRpZXMiOlsiUk9MRV9BRE1JTiIsIlJPTEVfVVNFUiJdLCJqdGkiOiJlYTJhZGRjZC1jN2VlLTQ0MWUtYTdiNi05ZTQ2NTc1MDJjZGUiLCJjbGllbnRfaWQiOiJ3ZWJfYXBwIn0.LUDpv6ZOwq4T1u9HUU1oB1-JeU6TsjXgA5mfBwNXi3GFt8NAJzGKyrZWjYWoc2ajmLyYebZTTtN4qsX3fLxQY-toIw_utUA9XeoQEu8wG9Od3xm-rFjrTzatfH1CZVGvXV4mSvsf9wdLteTqV2_Fk_CaCAW8ycOLrcC8q8-I3gS_NnfaC8M9HExwiU9PO4GaddEmqsnqQ9hG3T0MftIvxCeFXNknLgCkmCFxXFpfJdmOxesmi0HKq482fsfv-hQQUwhrSnMopr6_xnfHXcx24FuO56IPSOIRHwxELMPK4owsKSvx1X9gSG2AVnd8L6LOR_vCJgDgKgjS1TicmoFxCA
解析后为:
{
"user_name": "admin",
"scope": [
"openid"
],
"exp": 1539001334,
"iat": 1539001244,
"authorities": [
"ROLE_ADMIN",
"ROLE_USER"
],
"jti": "773967ae-580d-4352-b459-ccb95d2ed20d",
"client_id": "web_app"
}
refresh token:
eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VyX25hbWUiOiJhZG1pbiIsInNjb3BlIjpbIm9wZW5pZCJdLCJhdGkiOiJlYTJhZGRjZC1jN2VlLTQ0MWUtYTdiNi05ZTQ2NTc1MDJjZGUiLCJleHAiOjE1MzkwMDQ5NDYsImlhdCI6MTUzOTAwMTM0NiwiYXV0aG9yaXRpZXMiOlsiUk9MRV9BRE1JTiIsIlJPTEVfVVNFUiJdLCJqdGkiOiJhNzUzZTYyNS00NzVmLTQ0N2QtYmVhMC0xNWIxZmJmMTI5OWQiLCJjbGllbnRfaWQiOiJ3ZWJfYXBwIn0.EQjamSqLiEg4-LTWcTUC1vNl9xr_B0n40RE-rDMMTKzwp_TJcJpa4WXRmJ0cqvejY7q-SdBOahkSqTsOA7HYtlnhrXo12QYqKWa12lgJ7dOBO4hdW0LS1-MRFBKAlXXG-I-hjrupVjDW0fBoBWVEmKtpfarp3gCPEKFBsJD75ZRlP_IDluN3Rw1zS1WqvtJ0rLT70Uijyf1c9S6On4KVPRRzI-6_jG9pA3E3PAE4fWGOw0FOljeomlhBU1IFROfcXWvJkDYOOrfGo1rOGaSAaTZ8FTi_9pamF2QSoo1zbVD1xWEFwT-ZKJ7yC2xCjrWmYthZy03clG1kA_3XY25y3w
解析后:
{
"user_name": "admin",
"scope": [
"openid"
],
"ati": "ea2addcd-c7ee-441e-a7b6-9e4657502cde",
"exp": 1539004946,
"iat": 1539001346,
"authorities": [
"ROLE_ADMIN",
"ROLE_USER"
],
"jti": "a753e625-475f-447d-bea0-15b1fbf1299d",
"client_id": "web_app"
}
结果分析:通过刷新前后refresh token的jti不一致不难看出,refresh token也已随着access token发生了更新。已经变成了两个不同的refresh token了,同时refresh token的过期时间也在一直往后延续,不在是初次生成refresh token时的过期时间了。
所以,在这种模式下,只要在refresh token有效期内执行操作,用户无需再次登陆。
关键代码:
包名 org.springframework.security.oauth2.provider.token.store 类名:JwtAccessTokenConverter 类
public OAuth2AccessToken enhance(OAuth2AccessToken accessToken, OAuth2Authentication authentication) {
DefaultOAuth2AccessToken result = new DefaultOAuth2AccessToken(accessToken);
Map info = new LinkedHashMap(accessToken.getAdditionalInformation());
String tokenId = result.getValue();
if (!info.containsKey(TOKEN_ID)) {
info.put(TOKEN_ID, tokenId);
}
else {
tokenId = (String) info.get(TOKEN_ID);
}
result.setAdditionalInformation(info);
result.setValue(encode(result, authentication));
OAuth2RefreshToken refreshToken = result.getRefreshToken();
if (refreshToken != null) {
DefaultOAuth2AccessToken encodedRefreshToken = new DefaultOAuth2AccessToken(accessToken);
encodedRefreshToken.setValue(refreshToken.getValue());
// Refresh tokens do not expire unless explicitly of the right type
encodedRefreshToken.setExpiration(null);
try {
Map claims = objectMapper
.parseMap(JwtHelper.decode(refreshToken.getValue()).getClaims());
if (claims.containsKey(TOKEN_ID)) {
encodedRefreshToken.setValue(claims.get(TOKEN_ID).toString());
}
}
catch (IllegalArgumentException e) {
}
Map refreshTokenInfo = new LinkedHashMap(
accessToken.getAdditionalInformation());
refreshTokenInfo.put(TOKEN_ID, encodedRefreshToken.getValue());
refreshTokenInfo.put(ACCESS_TOKEN_ID, tokenId);
encodedRefreshToken.setAdditionalInformation(refreshTokenInfo);
DefaultOAuth2RefreshToken token = new DefaultOAuth2RefreshToken(
encode(encodedRefreshToken, authentication));
if (refreshToken instanceof ExpiringOAuth2RefreshToken) {
Date expiration = ((ExpiringOAuth2RefreshToken) refreshToken).getExpiration();
encodedRefreshToken.setExpiration(expiration);
token = new DefaultExpiringOAuth2RefreshToken(encode(encodedRefreshToken, authentication), expiration);
}
result.setRefreshToken(token);
}
return result;
}
包名 org.springframework.security.oauth2.provider.token ;类名:DefaultTokenServices类
@Transactional(noRollbackFor={InvalidTokenException.class, InvalidGrantException.class})
public OAuth2AccessToken refreshAccessToken(String refreshTokenValue, TokenRequest tokenRequest)
throws AuthenticationException {
if (!supportRefreshToken) {
throw new InvalidGrantException("Invalid refresh token: " + refreshTokenValue);
}
OAuth2RefreshToken refreshToken = tokenStore.readRefreshToken(refreshTokenValue);
if (refreshToken == null) {
throw new InvalidGrantException("Invalid refresh token: " + refreshTokenValue);
}
OAuth2Authentication authentication = tokenStore.readAuthenticationForRefreshToken(refreshToken);
if (this.authenticationManager != null && !authentication.isClientOnly()) {
// The client has already been authenticated, but the user authentication might be old now, so give it a
// chance to re-authenticate.
Authentication user = new PreAuthenticatedAuthenticationToken(authentication.getUserAuthentication(), "", authentication.getAuthorities());
user = authenticationManager.authenticate(user);
Object details = authentication.getDetails();
authentication = new OAuth2Authentication(authentication.getOAuth2Request(), user);
authentication.setDetails(details);
}
String clientId = authentication.getOAuth2Request().getClientId();
if (clientId == null || !clientId.equals(tokenRequest.getClientId())) {
throw new InvalidGrantException("Wrong client for this refresh token: " + refreshTokenValue);
}
// clear out any access tokens already associated with the refresh
// token.
tokenStore.removeAccessTokenUsingRefreshToken(refreshToken);
if (isExpired(refreshToken)) {
tokenStore.removeRefreshToken(refreshToken);
throw new InvalidTokenException("Invalid refresh token (expired): " + refreshToken);
}
authentication = createRefreshedAuthentication(authentication, tokenRequest);
if (!reuseRefreshToken) {
tokenStore.removeRefreshToken(refreshToken);
refreshToken = createRefreshToken(authentication);
}
OAuth2AccessToken accessToken = createAccessToken(authentication, refreshToken);
tokenStore.storeAccessToken(accessToken, authentication);
if (!reuseRefreshToken) {
tokenStore.storeRefreshToken(accessToken.getRefreshToken(), authentication);
}
return accessToken;
}
@Transactional
public OAuth2AccessToken createAccessToken(OAuth2Authentication authentication) throws AuthenticationException {
OAuth2AccessToken existingAccessToken = tokenStore.getAccessToken(authentication);
OAuth2RefreshToken refreshToken = null;
if (existingAccessToken != null) {
if (existingAccessToken.isExpired()) {
if (existingAccessToken.getRefreshToken() != null) {
refreshToken = existingAccessToken.getRefreshToken();
// The token store could remove the refresh token when the
// access token is removed, but we want to
// be sure...
tokenStore.removeRefreshToken(refreshToken);
}
tokenStore.removeAccessToken(existingAccessToken);
}
else {
// Re-store the access token in case the authentication has changed
tokenStore.storeAccessToken(existingAccessToken, authentication);
return existingAccessToken;
}
}
// Only create a new refresh token if there wasn't an existing one
// associated with an expired access token.
// Clients might be holding existing refresh tokens, so we re-use it in
// the case that the old access token
// expired.
if (refreshToken == null) {
refreshToken = createRefreshToken(authentication);
}
// But the refresh token itself might need to be re-issued if it has
// expired.
else if (refreshToken instanceof ExpiringOAuth2RefreshToken) {
ExpiringOAuth2RefreshToken expiring = (ExpiringOAuth2RefreshToken) refreshToken;
if (System.currentTimeMillis() > expiring.getExpiration().getTime()) {
refreshToken = createRefreshToken(authentication);
}
}
OAuth2AccessToken accessToken = createAccessToken(authentication, refreshToken);
tokenStore.storeAccessToken(accessToken, authentication);
// In case it was modified
refreshToken = accessToken.getRefreshToken();
if (refreshToken != null) {
tokenStore.storeRefreshToken(refreshToken, authentication);
}
return accessToken;
}
从上面的源码可以分析,在refreshAccessToken()方法中,代码在询问refresh token是否需要重新生成;
在enhance()方法中,代码在给refresh token添加access token关联信息ati,让后再次加密。