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);
}
-
- 當客戶端帶着
header token訪問oauth2資源服務器,資源服務器會自動攔截token
- 當客戶端帶着
-
- 發送
token到 認證服務器 校驗token合法性
- 發送
-
- 若認證服務器返回給資源服務器是
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;
}
-
- 當我們通過oauth2 去獲取
token時,若當前用戶已經存在對應的token,直接返回而不不會創建新 token。
- 當我們通過oauth2 去獲取
-
- 這就意味着,雖然設置了對應客戶端獲取 token 的有效時間,這里獲取到的
token。
若是已下發舊token,有效時間不會和session機制一樣自動續期。
- 這就意味着,雖然設置了對應客戶端獲取 token 的有效時間,這里獲取到的
-
- 綜上情況,在操作過程中
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;
}
客戶端(前端)何時刷新
被動刷新

-
客戶端攜帶
token訪問資源服務器資源 -
資源服務器攔截
token去認證服務器check_token -
認證服務器返回
token過期錯誤,資源服務器包裝錯誤信息返回給客戶端 -
客戶端根據返回錯誤信息(響應碼),直接調用認證服務器
refresh_token -
認證服務器返回新的
token給客戶端, 然后再次發起 資源調用
被動請求的缺點是,用戶當次請求會失敗(返回token失敗),對一些業務連貫的操作不是很友好
主動刷新

-
客戶端存在計算邏輯,計算下發token 有效期
-
若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 權限管理系統
