问题1
原报错信息
Access to XMLHttpRequest at 'http://127.0.0.1:8081/web/follow/1480460281762021377/true' from origin 'http://localhost:8082' has been blocked by CORS policy: Response to preflight request doesn't pass access control check: It does not have HTTP ok status.
先看拦截器的代码
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler){
log.debug("认证请求进入拦截器前[{}]",request.getRequestURI());
String token = request.getHeader(Constant.WEB_TOKEN_NAME);
// 在这个地方死活获取不到Token的信息、一直为null
log.debug("前端传入Token[{}]",token);
if (StrUtil.isEmpty(token) || "null".equals(token)){
throw new AuthException(ResponseCode.TOKEN_ERROR);
}
if (!JwtUtil.validateToken(token)) {
throw new AuthException(ResponseCode.TOKEN_OVERDUE);
}
if (!sessionAccountManager.isWebAccountCacheValid(token,response)){
throw new AuthException(ResponseCode.TOKEN_OVERDUE);
}
return true;
}
实现是在做一个Token认证的时候出现的问题、已目前的代码来看当进入到拦截器时、Token一直为空然后抛出异常、然后前端axios做数据的响应得到一个响应结果、但是axios做响应的时候前端就出错了。
这是时候就不是跨域的问题了、而是在执行真正的请求前会发送一个OPTIONS方式嗅探请求
、就是因为发送了一个这个请求、这个请求中不没携带Constant.WEB_TOKEN_NAME
的值、所以在这个请求中就会直接抛出异常、导致这个嗅探失败从而导致前端报错。
解决方法
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler){
log.debug("认证请求进入拦截器前[{}]、请求方式[{}]",request.getRequestURI(),request.getMethod());
// 将OPTIONS请求的方式直接放行
if ("OPTIONS".equals(request.getMethod())){
return true;
}
String token = request.getHeader(Constant.WEB_TOKEN_NAME);
log.debug("前端传入Token[{}]",token);
if (StrUtil.isEmpty(token) || "null".equals(token)){
throw new AuthException(ResponseCode.TOKEN_ERROR);
}
if (!JwtUtil.validateToken(token)) {
throw new AuthException(ResponseCode.TOKEN_OVERDUE);
}
if (!sessionAccountManager.isWebAccountCacheValid(token,response)){
throw new AuthException(ResponseCode.TOKEN_OVERDUE);
}
return true;
}
问题2
要做一个Token延期
的操作在请求通过的时候、判断我们的Token
是否需要延期、如果要延期将我们延后生成后的Token
塞响应请求头中(Header)、通过axios
拿到响应数据。
先看代码
/**
* 验证token是否存在缓存中
*
* @param token token
* @return true 存在 false不存在
*/
public Boolean isWebAccountCacheValid(String token, HttpServletResponse response) {
SessionAccount sessionAccount = (SessionAccount) RedisUtil.get(buildWebAuthKeyString(token));
if (null == sessionAccount) {
return false;
} else {
if (JwtUtil.isTokenExpirationTimeArrive(token)) {
log.info("token在延期时间内开始延期!!!");
RedisUtil.delete(buildWebAuthKeyString(token));
String newToken = JwtUtil.generateToken(BeanUtil.toBean(sessionAccount, SessionAccount.class));
bindWebAuthAccountInfo(sessionAccount, newToken);
// 将新Token塞入响应请求头中
response.setHeader("newToken",newToken);
}
AccountUtils.setAccountId(sessionAccount);
return true;
}
}
前端代码
// 3.响应拦截器
service.interceptors.response.use(
response => {
// 拿到对象中的数据
const { config, data, headers, status } = response;
}
看似毫无问题、但是你输出response
响应的对象你会发现根本没有newToken
这个的这个响应请求头
需要这样做
/**
* 验证token是否存在缓存中
*
* @param token token
* @return true 存在 false不存在
*/
public Boolean isWebAccountCacheValid(String token, HttpServletResponse response) {
SessionAccount sessionAccount = (SessionAccount) RedisUtil.get(buildWebAuthKeyString(token));
if (null == sessionAccount) {
return false;
} else {
if (JwtUtil.isTokenExpirationTimeArrive(token)) {
log.info("token在延期时间内开始延期!!!");
RedisUtil.delete(buildWebAuthKeyString(token));
String newToken = JwtUtil.generateToken(BeanUtil.toBean(sessionAccount, SessionAccount.class));
bindWebAuthAccountInfo(sessionAccount, newToken);
// 将新Token塞入响应请求头中
response.setHeader("newToken",newToken);
// 需要加上这个请求头value是你想要在axios响应中拿到的请求头的key
response.setHeader("Access-Control-Expose-Headers","newToken");
// 显示多个可以用逗号隔开
// response.setHeader("Access-Control-Expose-Headers","newToken,xxxx");
}
AccountUtils.setAccountId(sessionAccount);
return true;
}
}
加上了response.setHeader("Access-Control-Expose-Headers","newToken");
前端就能打印出newToken
这个