上篇讲了登陆一些基础代码,也说到,没有拦截器的登陆不是完整的登陆,所以这篇讲如何优雅写登陆拦截器
需要两个:拦截器和拦截适配器
其中,
拦截器需要实现HandlerInterceptor,并实现preHandle方法
适配器需要实现WebMvcConfigurer,并实现addInterceptors方法
拦截器通常放在intercepter包下面,拦截适配器通常放在config包下面
入参(实现的时候自动会带):HttpServletRequest、HttpServletResponse、Object
1、从request的header中拿到token
判断:token是否为null或是否为空。如果是,打印出错误码(code)和错误信息(message)。可返回“登陆失效,请重新登陆”------token为空,并返回false
2、解析传入的token,得到后台userid
判断:userid是否为空。如果是,打印出错误码(code)和错误信息(message)。可返回“令牌无效,请重新登录”------token无效:有值但是解析失败,并返回false
3、获取redis中的token
判断:redis中的token是否为null或者redis中的token是否不等于传过来的token,打印出错误码(code)和错误信息(message)。可返回“您已在别处登录”------token过期:解析成功,但是和redis不一致,并返回false
4、返回true
代码在此
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) {
String token = request.getHeader("token");
//判断是否有传token
if (token == null || "".equals(token)) {
System.out.print("code="+4000+"message=token为空")
return false;
}
//解析传入的token,获得后台user_id
String userId = JwtToken.getUserId(token);// 这个上篇文章讲过
if (userId == null) {
System.out.print("code="+4001+"message=token无效:有值但是解析失败")
return false;
}
//获取redis中的token
String redisTokenKey = String.format(GODZILLA:ADMIN:TOKEN:%s, userId);
String redisToken = (String) redisTemplate.opsForValue().get(redisTokenKey);
if (redisToken == null || !redisToken.equals(token)) {
System.out.print("code="+4002+"message=token过期:解析成功,但是和redis不一致")
return false;
}
return true;
}
入参:InterceptorRegistry
1、首先类中生成前台和后台用户的拦截器对象
2、然后重写addInterceptors方法
话不多说,直接上代码
@Bean
public AAALoginInterceptor getAAALoginInterceptor() {
// 这个对应自己的拦截器就ok
return new AAALoginInterceptor();
}
@Bean
public BBBLoginInterceptor getBBBLoginInterceptor() {
return new BBBLoginInterceptor();
}
public void addInterceptors(InterceptorRegistry registry) {
// 前台登录用户拦截器
registry.addInterceptor(getAAALoginInterceptor()) // 添加拦截器(这个拦截器是getAAALoginInterceptor这个方法的返回,即getAAALoginInterceptor对象)
.addPathPatterns("/**")// 拦截所有url
.excludePathPatterns(getCommonExcludePath()) // 除了getCommonExcludePath这个方法中返回的路径
.excludePathPatterns(getFrontExcludePath());// 除了getFrontExcludePath这个方法中返回的路径
// 后台登录用户拦截器
registry.addInterceptor(getBBBLoginInterceptor())
.addPathPatterns("/**")
.excludePathPatterns(getCommonExcludePath())
.excludePathPatterns(getAdminExcludePath());
}
/**
* 统一跳过验证的Path
* @return
*/
private List<String> getCommonExcludePath() {
List<String> list = new ArrayList<>();
list.add("/error");// 以error开头的所有url
list.add("/swagger-resources/**");//swagger-resources开头的 url
list.add("/swagger-ui.html/**");// 以swagger-ui.html开头的所有url
return list;
}
private List<String> getBBBExcludePath() {
List<String> list = new ArrayList<>();
list.add("/aaa/**");// 以aaa开头的所有url
list.add("/bbb/**"); //以bbbf开头的所有url//todo
list.add("/bbb/login");// 密码/验证码登录接口 // 以/bbb/login开头的所有url
list.add("/bbb/code");// 后台获取验证码 // 以/bbb/code开头的所有url
return list;
}
下面的代码同理
private List<String> getAAAExcludePath() {
List<String> list = new ArrayList<>();
list.add("/bbb/**");
list.add("/aaa/**");
list.add("/aaa/login");//密码登录接口
list.add("/aaa/user/verify_code");//验证码登录
list.add("/aaa/send_vcode");//发送验证码
return list;
}
补充一个坑哈:
前端在调试登陆接口的时候用token直接登陆,爆出了跨域的错误,后来经排查,我们项目中有用到两个token,一个AAAtoken,一个BBBtoken(我的这个是BBBtoken),跨域只放行了AAAtoken,这时候需要加上BBBtoken才可以
response.setHeader("Access-Control-Allow-Headers", "Origin,AAAtoken,BBBToken,x-requested-with,Content-Type,Accept");
类似于这样