前端校驗(yàn)請求
/src/page/index/index.vue
refreshToken() {
this.refreshTime = setInterval(() => {
checkToken(this.refreshLock, this.$store)
}, 10000)
}
checkToken
/**
* 校驗(yàn)令牌塘辅,若有效期小于半小時(shí)自動續(xù)期
*
* 定時(shí)任務(wù)請求后端接口返回實(shí)際的有效時(shí)間,不進(jìn)行本地計(jì)算避免 客戶端和服務(wù)器機(jī)器時(shí)鐘不一致
* @param refreshLock
*/
export const checkToken = (refreshLock, $store) => {
const token = store.getters.access_token
// 獲取當(dāng)前選中的 basic 認(rèn)證信息
let basicAuth = getStore({name: 'basicAuth'})
if(validatenull(token) || validatenull(basicAuth)){
return;
}
request({
url: '/auth/token/check_token',
headers: {
isToken: false,
Authorization: basicAuth
},
method: 'get',
params: {token}
}).then(response => {
const expire = response && response.data && response.data.exp
if (expire) {
const expiredPeriod = expire * 1000 - new Date().getTime()
console.log('當(dāng)前token過期時(shí)間', expiredPeriod, '毫秒')
//小于半小時(shí)自動續(xù)約
if (expiredPeriod <= website.remainingTime) {
if (!refreshLock) {
refreshLock = true
$store.dispatch('RefreshToken')
.catch(() => {
clearInterval(this.refreshTime)
})
refreshLock = false
}
}
}
}).catch(error => {
console.error(error)
})
}
流程
當(dāng)用戶攜帶token 請求資源服務(wù)器的資源時(shí),Spring Security 攔截token蒜鸡,進(jìn)行token 和 userdetails 匹配過程粪小,把無狀態(tài)的token 轉(zhuǎn)化成具體用戶
BearerTokenAuthenticationFilter
https://docs.spring.io/spring-security/site/docs/current/api/org/springframework/security/oauth2/server/resource/web/BearerTokenAuthenticationFilter.html
作為一個(gè)OncePerRequestFilter
核心邏輯在doFilterInternal
中斯议。
@Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain)
throws ServletException, IOException {
String token;
try {
token = this.bearerTokenResolver.resolve(request);
}
catch (OAuth2AuthenticationException invalid) {
this.logger.trace("Sending to authentication entry point since failed to resolve bearer token", invalid);
this.authenticationEntryPoint.commence(request, response, invalid);
return;
}
if (token == null) {
this.logger.trace("Did not process request since did not find bearer token");
filterChain.doFilter(request, response);
return;
}
BearerTokenAuthenticationToken authenticationRequest = new BearerTokenAuthenticationToken(token);
authenticationRequest.setDetails(this.authenticationDetailsSource.buildDetails(request));
try {
AuthenticationManager authenticationManager = this.authenticationManagerResolver.resolve(request);
Authentication authenticationResult = authenticationManager.authenticate(authenticationRequest);
SecurityContext context = SecurityContextHolder.createEmptyContext();
context.setAuthentication(authenticationResult);
SecurityContextHolder.setContext(context);
this.securityContextRepository.saveContext(context, request, response);
if (this.logger.isDebugEnabled()) {
this.logger.debug(LogMessage.format("Set SecurityContextHolder to %s", authenticationResult));
}
filterChain.doFilter(request, response);
}
catch (AuthenticationException failed) {
SecurityContextHolder.clearContext();
this.logger.trace("Failed to process authentication request", failed);
this.authenticationFailureHandler.onAuthenticationFailure(request, response, failed);
}
}
1.攔截請求進(jìn)行鑒權(quán)
BearerTokenAuthenticationFilter
攔截所有資源服務(wù)器的請求删顶。
解析 header 或者參數(shù)中的 access_token 字段
根據(jù)access_token 構(gòu)造出來 BearerTokenAuthenticationToken
認(rèn)證對象
請求authenticationManager.authenticate(authenticationRequest);
進(jìn)行鑒權(quán)雹舀。
2.鑒權(quán)操作
BearerTokenAuthenticationFilter
解析 Authentication: Bearer {token} 中的token,交給 OpaqueTokenAuthenticationProvider
@Override
public Authentication authenticate(Authentication authentication) throws AuthenticationException {
if (!(authentication instanceof BearerTokenAuthenticationToken)) {
return null;
}
BearerTokenAuthenticationToken bearer = (BearerTokenAuthenticationToken) authentication;
OAuth2AuthenticatedPrincipal principal = getOAuth2AuthenticatedPrincipal(bearer);
AbstractAuthenticationToken result = convert(principal, bearer.getToken());
result.setDetails(bearer.getDetails());
this.logger.debug("Authenticated token");
return result;
}
private OAuth2AuthenticatedPrincipal getOAuth2AuthenticatedPrincipal(BearerTokenAuthenticationToken bearer) {
try {
return this.introspector.introspect(bearer.getToken());
}
catch (BadOpaqueTokenException failed) {
this.logger.debug("Failed to authenticate since token was invalid");
throw new InvalidBearerTokenException(failed.getMessage(), failed);
}
catch (OAuth2IntrospectionException failed) {
throw new AuthenticationServiceException(failed.getMessage(), failed);
}
}
OpaqueTokenAuthenticationProvider
委托 OpaqueTokenIntrospector
的 introspect
去校驗(yàn) token著恩。
PigRedisOAuth2AuthorizationService
通過token value 查詢 認(rèn)證中心下發(fā)令牌時(shí) 存儲的用戶認(rèn)證信息.
調(diào)用
RedisOAuth2AuthorizationService
的findByToken
@Override
@Nullable
public OAuth2Authorization findByToken(String token, @Nullable OAuth2TokenType tokenType) {
Assert.hasText(token, "token cannot be empty");
Assert.notNull(tokenType, "tokenType cannot be empty");
redisTemplate.setValueSerializer(RedisSerializer.java());
return (OAuth2Authorization) redisTemplate.opsForValue().get(buildKey(tokenType.getValue(), token));
}