jwt 实现token 拦截器bug +前端路由白名单bug总结

项目场景:

提示:这里简述项目相关背景:

这两天一直在做token相关的模块 总结一下遇到的几个bug以及解决方案


问题描述

提示:这里描述项目中遇到的问题:

1.使用jwt 实现token的时候关于token码的实现主要操作工具类就行了,但是更多的问题出现在拦截器的方面,下面放一下拦截器的部分拦截代码

这是配置类相关的

 /**
     * 配置拦截器、拦截路径
     * 每次请求到拦截的路径,就回去执行拦截器中的方法
     * @param registry
     */
    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        List<String> excludePath = new ArrayList<>();

        // 排除拦截,除了注册登录(此时还没TOKEN),其他都拦截
        excludePath.add("/register"); // 登录
        excludePath.add("/login/**"); // 注册
        excludePath.add("/select/**");
        excludePath.add("/static/**"); // 把静态资源的访问也排除
        excludePath.add("/assets/**"); // 把静态资源的访问也排除

        registry.addInterceptor(tokenInterceptor)
             .addPathPatterns("/**") ;// 添加拦截的路径模式 /** 代表全部拦截
               .excludePathPatterns(excludePath); //这是排除拦截的代码

        WebMvcConfigurer.super.addInterceptors(registry);
    }

这是拦截器相关代码

``

package com.example.demo.util;

import lombok.extern.slf4j.Slf4j;
import org.json.JSONObject;
import org.springframework.stereotype.Component;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.servlet.HandlerInterceptor;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.lang.reflect.Method;

/**
 * @author wcc
 * @date 2021/11/19 11:13
 */
@Component
@Slf4j
public class TokenInterceptor implements HandlerInterceptor {

    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {

        // 如果不是映射到方法直接通过
        if (!(handler instanceof HandlerMethod)) {
            return true;
        }

        HandlerMethod handlerMethod = (HandlerMethod) handler;
        Method method = handlerMethod.getMethod();

        System.out.println(method);
        //跨域请求会首先发送一个options请求,直接返回正常状态并通过拦截器
        if (request.getMethod().equalsIgnoreCase("OPTIONS")) {
            response.setStatus(HttpServletResponse.SC_OK);
            log.info("走过了option请求");
            return true;
        }

        // 检查该方法上是否有 PassToken 的注解
        System.out.println(method.isAnnotationPresent(PassToken.class));
        if (method.isAnnotationPresent(PassToken.class)) {
            PassToken passToken = method.getAnnotation(PassToken.class);
            if (passToken.required()) {
                return true;
            }
        }

        System.out.println(method.isAnnotationPresent(UserLoginToken.class));
        // 检查该方法是否有 UserLoginToken 权限的注解
        if (method.isAnnotationPresent(UserLoginToken.class)) {
            UserLoginToken annotation = method.getAnnotation(UserLoginToken.class);
            if (annotation.required()) {
                response.setCharacterEncoding("UTF-8");

                String token = request.getHeader("token");
                System.out.println(token);
                if (token != null) {
                    //这里,其实少了一些步骤,没有连接数据库的缘故
                    /**
                     * 具体操作是这样实现的,创建 TOKEN 的时候可以根据用户的ID 来去创建
                     * 所以当验证 TOKEN 不为空时获取其中的数据ID 并查找数据库看是否存在
                     * 如果存在再去验证 TOKEN
                     */
                    boolean result = TokenUtils.verfiry(token);
                    if (result) {
                        log.info("TOKEN 验证通过,TokenInterceptor拦截器放行");
                        return true;
                    }
                }
            }
        }

        response.setContentType("application/json;charset=utf8");

        try {
            JSONObject jsonObject = new JSONObject();
            jsonObject.put("message", "您没有权限");
            jsonObject.put("code", HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
            response.getWriter().append(jsonObject.toString());
            log.info("验证失败,未通过TokenInterceptor拦截器");
        } catch (Exception e) {
            log.error(e.getMessage());
            return false;
        }

        //后面还可以进行其他判断 用户是否存在等等,我这里就不使用数据库了 不再进行判断

        return false;
    }
}


2.前端实现token的验证也比较简单 更多的问题是出现在路由白名单的地方
,我主要出现的问题就是路由白名单只能设定一个路由,无法设定多个路由白名单

原因分析:

针对问题1的分析尝试了很多,后来发现不是什么大问题,在后端拦截器中,每当前端有axios请求的时候,如果后端在处理的时候出现异常就会进行拦截,这样拦截对于用户使用是很友好的,可以避免很多非法请求,但是对于开发者来说,这样进行拦截的对bug分析有阻碍左右,无论什么错误请求后端都会发送同样的错误码,而且无法请求到服务器内部,无法分析错误原因

针对问题2的分析就是看一下main.js中的路由拦截器

router.beforeEach((to, from, next) => {
  document.title = `${to.meta.title} | 后台管理系统`
  const whiteRouter = ['Login', 'register']
  const token = getToken()
  // 判断是否存在token 有token则放行
  if (token) {
    next()
  } else if (whiteRouter.indexOf(to.name) !== -1) {
    // indexOf方法,判断数组中是否存在指定的某个对象,如果不存在,则返回-1
    nprogress.start() // 这是一个进度条方法
    next()
  } else {
    next('/login') // 验证不通过重定向到login
  }

解决方案:

问题1

    public void addInterceptors(InterceptorRegistry registry) {
        List<String> excludePath = new ArrayList<>();

        // 排除拦截,除了注册登录(此时还没TOKEN),其他都拦截
        excludePath.add("/register"); // 登录
        excludePath.add("/login/**"); // 注册
        excludePath.add("/select/**");
        excludePath.add("/static/**"); // 把静态资源的访问也排除
        excludePath.add("/assets/**"); // 把静态资源的访问也排除

        registry.addInterceptor(tokenInterceptor)
//                .addPathPatterns("/**") ;// 添加拦截的路径模式 /** 代表全部拦截
               .excludePathPatterns("/**");

        WebMvcConfigurer.super.addInterceptors(registry);
    }

可以放行所有拦截让拦截器暂时实现,等项目完全可以跑通再加入拦截器

问题2
解决方法利用 indexOf方法,判断数组中是否存在指定的某个对象,如果不存在,则返回-1
其中做判断的时候if中的条件最好不要超过两个
超过两个可能会有Maximum call stack size exceeded 错误
也就是:超过最大调用堆栈大小
具体原因可以参照jvm虚拟机
也就是说可能出现栈溢出的情况


```javascript
router.beforeEach((to, from, next) => {
  document.title = `${to.meta.title} | 后台管理系统`
  const whiteRouter = ['Login', 'register']
  const token = getToken()
  // 判断是否存在token 有token则放行
  if (token) {
    next()
  } else if (whiteRouter.indexOf(to.name) !== -1) {
    // indexOf方法,判断数组中是否存在指定的某个对象,如果不存在,则返回-1
    nprogress.start() // 这是一个进度条方法
    next()
  } else {
    next('/login') // 验证不通过重定向到login
  }

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值