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 權限管理系統