## 前端校验请求
/src/page/index/index.vue
```
refreshToken() {
this.refreshTime = setInterval(() => {
checkToken(this.refreshLock, this.$store)
}, 10000)
}
```
`checkToken`
```
/**
* 校验令牌,若有效期小于半小时自动续期
*
* 定时任务请求后端接口返回实际的有效时间,不进行本地计算避免 客户端和服务器机器时钟不一致
* @param refreshLock
*/
export const checkToken = (refreshLock, $store) => {
const token = store.getters.access_token
// 获取当前选中的 basic 认证信息
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('当前token过期时间', expiredPeriod, '毫秒')
//小于半小时自动续约
if (expiredPeriod <= website.remainingTime) {
if (!refreshLock) {
refreshLock = true
$store.dispatch('RefreshToken')
.catch(() => {
clearInterval(this.refreshTime)
})
refreshLock = false
}
}
}
}).catch(error => {
console.error(error)
})
}
```
## 流程
>当用户携带token 请求资源服务器的资源时,Spring Security 拦截token,进行token 和 userdetails 匹配过程,把无状态的token 转化成具体用户
![image](https://img2022.cnblogs.com/blog/1901531/202211/1901531-20221124150952640-1953156075.png)
## BearerTokenAuthenticationFilter
https://docs.spring.io/spring-security/site/docs/current/api/org/springframework/security/oauth2/server/resource/web/BearerTokenAuthenticationFilter.html
作为一个`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.拦截请求进行鉴权
`BearerTokenAuthenticationFilter` 拦截所有资源服务器的请求。
解析 header 或者参数中的 access_token 字段
![image](https://img2022.cnblogs.com/blog/1901531/202211/1901531-20221124143131405-1773364520.png)
根据access_token 构造出来 `BearerTokenAuthenticationToken `认证对象
![image](https://img2022.cnblogs.com/blog/1901531/202211/1901531-20221124143305690-1170185900.png)
请求`authenticationManager.authenticate(authenticationRequest);`进行鉴权。
![image](https://img2022.cnblogs.com/blog/1901531/202211/1901531-20221124144022298-952097594.png)
### 2.鉴权操作
`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` 去校验 token。
`PigRedisOAuth2AuthorizationService` 通过token value 查询 认证中心下发令牌时 存储的用户认证信息.
![image](https://img2022.cnblogs.com/blog/1901531/202211/1901531-20221124145326378-1962013974.png)
调用`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));
}
```