使用Vue+SpringBoot拦截做Token认证出现It does not have HTTP ok status.

310 阅读2分钟

问题1

image.png 原报错信息

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这个