OAuth2.0關(guān)于刷新token的問題

通過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)豪筝。

  1. 在UAA開源項(xiàng)目中痰滋,refresh token有兩者使用模式:重復(fù)使用和非重復(fù)使用摘能。
    所謂重復(fù)使用指的是登陸后初次生成的refresh token一直保持不變,直到過期敲街;
    非重復(fù)使用指的是在每一次使用refresh token刷新access token的過程中团搞,refresh token也隨之更新,即生成新的refresh token多艇。

  2. 在UAA項(xiàng)目中這一設(shè)置是通過UaaConfiguration類reuseRefreshTokens(boolean b)(默認(rèn)為true)來設(shè)置完成逻恐;
    下面是代碼重現(xiàn)的結(jié)果分析:

  3. 首先分析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)储藐。

  1. 現(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,讓后再次加密矢渊。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末继准,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子矮男,更是在濱河造成了極大的恐慌移必,老刑警劉巖,帶你破解...
    沈念sama閱讀 217,084評(píng)論 6 503
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件毡鉴,死亡現(xiàn)場(chǎng)離奇詭異崔泵,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)猪瞬,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,623評(píng)論 3 392
  • 文/潘曉璐 我一進(jìn)店門憎瘸,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人陈瘦,你說我怎么就攤上這事幌甘。” “怎么了痊项?”我有些...
    開封第一講書人閱讀 163,450評(píng)論 0 353
  • 文/不壞的土叔 我叫張陵锅风,是天一觀的道長(zhǎng)。 經(jīng)常有香客問我线婚,道長(zhǎng)遏弱,這世上最難降的妖魔是什么盆均? 我笑而不...
    開封第一講書人閱讀 58,322評(píng)論 1 293
  • 正文 為了忘掉前任塞弊,我火速辦了婚禮,結(jié)果婚禮上泪姨,老公的妹妹穿的比我還像新娘游沿。我一直安慰自己,他們只是感情好肮砾,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,370評(píng)論 6 390
  • 文/花漫 我一把揭開白布诀黍。 她就那樣靜靜地躺著,像睡著了一般仗处。 火紅的嫁衣襯著肌膚如雪眯勾。 梳的紋絲不亂的頭發(fā)上枣宫,一...
    開封第一講書人閱讀 51,274評(píng)論 1 300
  • 那天,我揣著相機(jī)與錄音吃环,去河邊找鬼也颤。 笑死,一個(gè)胖子當(dāng)著我的面吹牛郁轻,可吹牛的內(nèi)容都是我干的翅娶。 我是一名探鬼主播,決...
    沈念sama閱讀 40,126評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼好唯,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼竭沫!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起骑篙,我...
    開封第一講書人閱讀 38,980評(píng)論 0 275
  • 序言:老撾萬榮一對(duì)情侶失蹤蜕提,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后替蛉,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體贯溅,經(jīng)...
    沈念sama閱讀 45,414評(píng)論 1 313
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,599評(píng)論 3 334
  • 正文 我和宋清朗相戀三年躲查,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了它浅。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 39,773評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡镣煮,死狀恐怖姐霍,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情典唇,我是刑警寧澤镊折,帶...
    沈念sama閱讀 35,470評(píng)論 5 344
  • 正文 年R本政府宣布,位于F島的核電站介衔,受9級(jí)特大地震影響恨胚,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜炎咖,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,080評(píng)論 3 327
  • 文/蒙蒙 一赃泡、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧乘盼,春花似錦升熊、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,713評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至粹胯,卻和暖如春蓖柔,著一層夾襖步出監(jiān)牢的瞬間辰企,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 32,852評(píng)論 1 269
  • 我被黑心中介騙來泰國(guó)打工况鸣, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留蟆豫,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 47,865評(píng)論 2 370
  • 正文 我出身青樓懒闷,卻偏偏與公主長(zhǎng)得像十减,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子愤估,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,689評(píng)論 2 354

推薦閱讀更多精彩內(nèi)容