通過refresh token刷新access token時(shí),refresh token也隨之更新的問題酷鸦?
項(xiàng)目背景:項(xiàng)目中使用SpringBoot集成OAuth2.0饰躲,實(shí)現(xiàn)對(duì)token的管理,此處包含兩種token類型臼隔,refresh token和access token嘹裂,兩者都具有有效期,在OAuth的設(shè)計(jì)模式中往往refresh token的有效期都比較長(zhǎng)(一般設(shè)置7天)摔握,而access token的有效期相對(duì)較短(一般為數(shù)個(gè)小時(shí))寄狼。所以在具體的實(shí)施過程中,當(dāng)access token過期而refresh token有效的時(shí)候氨淌,使用后者重新獲取新的access token泊愧。
問題描述:最近在使用UAA開源項(xiàng)目時(shí),發(fā)現(xiàn)每次使用refresh token刷新access token的時(shí)候盛正,不光access token更新了删咱,refresh token 同樣也更新了,深究了UAA和OAuth的源碼發(fā)現(xiàn)出現(xiàn)這種現(xiàn)象的原因由以下兩點(diǎn)豪筝。
在UAA開源項(xiàng)目中痰滋,refresh token有兩者使用模式:重復(fù)使用和非重復(fù)使用摘能。
所謂重復(fù)使用指的是登陸后初次生成的refresh token一直保持不變,直到過期敲街;
非重復(fù)使用指的是在每一次使用refresh token刷新access token的過程中团搞,refresh token也隨之更新,即生成新的refresh token多艇。在UAA項(xiàng)目中這一設(shè)置是通過UaaConfiguration類reuseRefreshTokens(boolean b)(默認(rèn)為true)來設(shè)置完成逻恐;
下面是代碼重現(xiàn)的結(jié)果分析:首先分析refresh token重復(fù)使用的情況,即在UaaConfiguration設(shè)置如下:
包名:com.yourcompany.uaa.config墩蔓,類名:UaaConfiguration類
@Override
public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
Collection<TokenEnhancer> tokenEnhancers = applicationContext.getBeansOfType(TokenEnhancer.class).values();
TokenEnhancerChain tokenEnhancerChain = new TokenEnhancerChain();
tokenEnhancerChain.setTokenEnhancers(new ArrayList<>(tokenEnhancers));
endpoints
.authenticationManager(authenticationManager)
.tokenStore(tokenStore())
.tokenEnhancer(tokenEnhancerChain)
//該字段設(shè)置設(shè)置refresh token是否重復(fù)使用,true:reuse;false:no reuse.
.reuseRefreshTokens(true);
}
用戶登陸獲壬颐А:同時(shí)獲取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解析之后的對(duì)應(yīng)的明文為:
{
"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解析之后的對(duì)應(yīng)的明文為:
{
"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過期之后,執(zhí)行刷新操作奸披,重新獲取如下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"
}
結(jié)果分析:
通過對(duì)比refresh token刷新前后的明文信息可知昏名,兩個(gè)refresh token的jti(jwt-token-id,token的唯一屬性)都為193a7c2d-4cf1-4af7-8119-1e9c9f916b6d阵面,說明兩個(gè)refresh token是同意個(gè)token轻局,并為更新。心細(xì)的讀者可能發(fā)現(xiàn)兩個(gè)refresh token的加密字符串是不同的样刷,其實(shí)這個(gè)原因是由于refresh token 中有個(gè)ati字段(access-token-id)信息仑扑,該字段是當(dāng)前access token的唯一Id,由于access token更新了(由刷新前后access token的jti不同可證明)置鼻,故而refresh token也相應(yīng)作出改變镇饮,但是其過期時(shí)間等關(guān)鍵信息并未改變。這個(gè)也可以從下面的關(guān)鍵代碼很明顯的看出箕母。
這種模式下的refresh token的過期仍然是以初次生成的時(shí)間為準(zhǔn)储藐。
- 現(xiàn)在介紹refresh token 非重復(fù)利用的場(chǎng)景:
非重復(fù)使用: 即當(dāng)access token 更新的時(shí)候,同時(shí)更新refresh token嘶是,這樣就把refresh token的過期時(shí)間一直往后延續(xù)钙勃。該模式的用意就是通過更新refresh token實(shí)現(xiàn)永遠(yuǎn)不需要再次登陸的目的。除非前后兩次對(duì)項(xiàng)目的操作時(shí)間間隔超出了refresh token的有效時(shí)間段聂喇。
下面看一下演示效果辖源,同樣設(shè)置UaaConfiguration,
@Override
public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
Collection<TokenEnhancer> tokenEnhancers = applicationContext.getBeansOfType(TokenEnhancer.class).values();
TokenEnhancerChain tokenEnhancerChain = new TokenEnhancerChain();
tokenEnhancerChain.setTokenEnhancers(new ArrayList<>(tokenEnhancers));
endpoints
.authenticationManager(authenticationManager)
.tokenStore(tokenStore())
.tokenEnhancer(tokenEnhancerChain)
//該字段設(shè)置設(shè)置refresh token是否重復(fù)使用,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過期之后希太,執(zhí)行刷新操作克饶,重新獲取如下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"
}
結(jié)果分析:通過刷新前后refresh token的jti不一致不難看出,refresh token也已隨著access token發(fā)生了更新誊辉。已經(jīng)變成了兩個(gè)不同的refresh token了彤路,同時(shí)refresh token的過期時(shí)間也在一直往后延續(xù),不在是初次生成refresh token時(shí)的過期時(shí)間了芥映。
所以洲尊,在這種模式下远豺,只要在refresh token有效期內(nèi)執(zhí)行操作,用戶無需再次登陸坞嘀。
關(guān)鍵代碼:
包名 org.springframework.security.oauth2.provider.token.store 類名:JwtAccessTokenConverter 類
public OAuth2AccessToken enhance(OAuth2AccessToken accessToken, OAuth2Authentication authentication) {
DefaultOAuth2AccessToken result = new DefaultOAuth2AccessToken(accessToken);
Map<String, Object> info = new LinkedHashMap<String, Object>(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<String, Object> claims = objectMapper
.parseMap(JwtHelper.decode(refreshToken.getValue()).getClaims());
if (claims.containsKey(TOKEN_ID)) {
encodedRefreshToken.setValue(claims.get(TOKEN_ID).toString());
}
}
catch (IllegalArgumentException e) {
}
Map<String, Object> refreshTokenInfo = new LinkedHashMap<String, Object>(
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關(guān)聯(lián)信息ati,讓后再次加密矢渊。