项目场景:
提示:这里简述项目相关背景:
这两天一直在做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
}