Token 校验逻辑
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
| @RequestMapping(value = "/oauth/check_token") @ResponseBody public Map<String, ?> checkToken(@RequestParam("token") String value) {
OAuth2AccessToken token = resourceServerTokenServices.readAccessToken(value); if (token == null) { throw new InvalidTokenException("Token was not recognised"); }
if (token.isExpired()) { throw new InvalidTokenException("Token has expired"); }
OAuth2Authentication authentication = resourceServerTokenServices.loadAuthentication(token.getValue());
return accessTokenConverter.convertAccessToken(token, authentication); }
|
- 当客户端带着 header token 访问 oauth2 资源服务器,资源服务器会自动拦截 token
- 发送 token 到 认证服务器 校验 token 合法性
- 若认证服务器返回给资源服务器是token不合法,则资源服务器返回给客户端对应的信息
Token 生成逻辑
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35
| 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(); tokenStore.removeRefreshToken(refreshToken); } tokenStore.removeAccessToken(existingAccessToken); } else { tokenStore.storeAccessToken(existingAccessToken, authentication); return existingAccessToken; } }
OAuth2AccessToken accessToken = createAccessToken(authentication, refreshToken); tokenStore.storeAccessToken(accessToken, authentication); refreshToken = accessToken.getRefreshToken(); if (refreshToken != null) { tokenStore.storeRefreshToken(refreshToken, authentication); } return accessToken;
}
|
- 当我们通过oauth2 去获取 token 时,若当前用户已经存在对应的token,直接返回而不不会创建新 token。
- 这就意味着,虽然设置了对应客户端获取 token 的有效时间,这里获取到的token。 若是已下发旧token,有效时间不会和session 机制一样自动续期。
- 综上情况,在操作过程中token 过期是一个常态化的问题。
Token 刷新逻辑
1 2 3 4 5
| 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
1 2 3 4 5 6 7 8 9
| 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
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
| @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 访问资源服务器资源
- 资源服务器拦截 token 去认证服务器 check_token
- 认证服务器返回 token 过期错误,资源服务器包装错误信息返回给客户端
- 客户端根据返回错误信息(响应码),直接调用认证服务器 refresh_token
- 认证服务器返回新的 token 给客户端, 然后再次发起 资源调用
被动请求的缺点是,用户当次请求会失败(返回token失败),对一些业务连贯的操作不是很友好
2. 主动刷新

- 客户端存在计算逻辑,计算下发token 有效期
- 若token要过期之前,主动发起刷新
主动请求的缺点是,客户端占用部分计算资源来处理 token 失效问题
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
| 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) },
|