聊聊 OAuth 2.0 的 Token 續期處理


Token 校驗邏輯

// CheckTokenEndpoint.checkToken
@RequestMapping(value = "/oauth/check_token")
@ResponseBody
public Map<String, ?> checkToken(@RequestParam("token") String value) {
    
    // 根據 token 查詢保存在 tokenStore 的令牌全部信息
	OAuth2AccessToken token = resourceServerTokenServices.readAccessToken(value);
	if (token == null) {
		throw new InvalidTokenException("Token was not recognised");
	}

	if (token.isExpired()) {
		throw new InvalidTokenException("Token has expired");
	}
    
    // 根據 token 查詢保存的 認證信息 還有權限角色等 (業務信息)
	OAuth2Authentication authentication = resourceServerTokenServices.loadAuthentication(token.getValue());

	return accessTokenConverter.convertAccessToken(token, authentication);
}
    1. 當客戶端帶着 header token 訪問 oauth2 資源服務器,資源服務器會自動攔截 token
    1. 發送 token 到 認證服務器 校驗 token 合法性
    1. 若認證服務器返回給資源服務器是token不合法,則資源服務器返回給客戶端對應的信息

Token 生成邏輯

    //DefaultTokenServices.createAccessToken 代碼邏輯
	public OAuth2AccessToken createAccessToken(OAuth2Authentication authentication) throws AuthenticationException {
        
        // 根據用戶信息(username),查詢已下發的token 
		OAuth2AccessToken existingAccessToken = tokenStore.getAccessToken(authentication);
		OAuth2RefreshToken refreshToken = null;
	    
	    // 存在已下發的token
		if (existingAccessToken != null) {
		    // 1. token 已經被標志過期,則刪除 
			if (existingAccessToken.isExpired()) {
				if (existingAccessToken.getRefreshToken() != null) {
					refreshToken = existingAccessToken.getRefreshToken();
					tokenStore.removeRefreshToken(refreshToken);
				}
				tokenStore.removeAccessToken(existingAccessToken);
			}
			else {
				// 直接返回存在的 token,並保存一下token 和 用戶信息的關系 (username)
				tokenStore.storeAccessToken(existingAccessToken, authentication);
				return existingAccessToken;
			}
		}

        // 不存在則創建新的 token
		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;

	}

    1. 當我們通過oauth2 去獲取 token 時,若當前用戶已經存在對應的token,直接返回而不不會創建新 token。
    1. 這就意味着,雖然設置了對應客戶端獲取 token 的有效時間,這里獲取到的token
      若是已下發舊token,有效時間不會和session 機制一樣自動續期。
    1. 綜上情況,在操作過程中token 過期是一個常態化的問題。

Token 刷新邏輯

curl --location --request POST 'http://auth-server/oauth/token?grant_type=refresh_token' \
--header 'Authorization: Basic dGVzdDp0ZXN0' \
--header 'VERSION: dev' \
--data-urlencode 'scope=server' \
--data-urlencode 'refresh_token=eccda61e-0c68-43af-8f67-6302cb389612'

若上,當 前端拿着正確的(未過期且未使用過)refresh_token 去調用 認證中心的刷新 端點刷新時,會 觸發RefreshTokenGranter, 返回新的 Token

public class RefreshTokenGranter extends AbstractTokenGranter {
	
	@Override
	protected OAuth2AccessToken getAccessToken(ClientDetails client, TokenRequest tokenRequest) {
		String refreshToken = tokenRequest.getRequestParameters().get("refresh_token");
		return getTokenServices().refreshAccessToken(refreshToken, tokenRequest);
	}
	
}

  • refreshAccessToken 代碼實現,調用 tokenStore 生成新的token
@Transactional(noRollbackFor={InvalidTokenException.class, InvalidGrantException.class})
	public OAuth2AccessToken refreshAccessToken(String refreshTokenValue, TokenRequest tokenRequest)
			throws AuthenticationException {

	
	   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;
	}

客戶端(前端)何時刷新

被動刷新

  1. 客戶端攜帶 token 訪問資源服務器資源

  2. 資源服務器攔截 token 去認證服務器 check_token

  3. 認證服務器返回 token 過期錯誤,資源服務器包裝錯誤信息返回給客戶端

  4. 客戶端根據返回錯誤信息(響應碼),直接調用認證服務器 refresh_token

  5. 認證服務器返回新的 token 給客戶端, 然后再次發起 資源調用

被動請求的缺點是,用戶當次請求會失敗(返回token失敗),對一些業務連貫的操作不是很友好

主動刷新

  1. 客戶端存在計算邏輯,計算下發token 有效期

  2. 若token要過期之前,主動發起刷新

主動請求的缺點是,客戶端占用部分計算資源來處理 token 失效問題

  // 10S檢測token 有效期
  refreshToken() {
    this.refreshTime = setInterval(() => {
      const token = getStore({
        name: 'access_token',
        debug: true
      })
      if (this.validatenull(token)) {
        return
      }
      if (this.expires_in <= 1000 && !this.refreshLock) {
        this.refreshLock = true
        this.$store
          .dispatch('RefreshToken')
          .catch(() => {
            clearInterval(this.refreshTime)
          })
        this.refreshLock = false
      }
      this.$store.commit('SET_EXPIRES_IN', this.expires_in - 10)
    }, 10000)
  },

『★★★★★』 基於Spring Boot 2.2、 Spring Cloud Hoxton & Alibaba、 OAuth2 的RBAC 權限管理系統

項目推薦: Spring Cloud 、Spring Security OAuth2的RBAC權限管理系統 歡迎關注


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM