来北京快两个月,目前工作稳定下来,所以继续帮学长写写公众号以及维护小程序了。今天主要结合之前遇到的问题来聊一下后台接口如何保存登陆信息。
这里我拿小程序的例子来做场景分析:在小程序最开始的版本中,所以的接口都是不需要认证就可以调用的,但是有一天学长说有人刷接口导致应用挂了好几次(可能是由于接口不小心在博客或者其他地方展示出去了,然后有人一直在爬数据),于是设置了拦截器,请求需要通过拦截器验证Token,通过Token获取相关信息,Token是使用JWT生成的,但是如何保存信息成了一个问题,因为接口的方法内部如果需要人员信息,再重新获取显然不合理,因为拦截器已经校验过且匹配到人员信息了。最开始我自己想的方案是使用Map存储,然后方法中通过Key去获取,但是Key不太好定义,对于API和前端都有一定的约束。最后学长给我一个方案:使用ThreadLocal来存储信息。
首先我们写一个测试的接口和拦截器,接口这里就不细说了,我们主要看拦截器: 首先配置拦截的资源,这里将所有的请求都拦截下来
@Configuration
public class WebConfig extends WebMvcConfigurationSupport {
@Bean
PriInterceptor priInterceptor() {
return new PriInterceptor();
}
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(priInterceptor())
.addPathPatterns("/**");
}
}
然后就是Interceptor,这里为了方便测试,我不使用Token,也不存放在header中,就把用户ID拼接在URL的后面
public class PriInterceptor implements HandlerInterceptor {
@Override
public boolean preHandle(HttpServletRequest httpServletRequest, HttpServletResponse response, Object handler) throws Exception {
//省去解析Token的步骤,假设这里ID是解析出来的
String ID = httpServletRequest.getParameter("ID");
System.out.println("ID:" + ID);
return true;
}
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, @Nullable ModelAndView modelAndView) throws Exception {
}
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, @Nullable Exception ex) throws Exception {
}
那么此时如何将ID存起来供接口方法调用呢?我们来写一个线程安全工具类来存储信息。
public class UserPool {
private static final ThreadLocal<Map<String, String>> local = new ThreadLocal<>();
public static void put(Map s) {
local.set(s);
}
public static Map get() {
return local.get();
}
public static void remove() {
Map<String, String> map = local.get();
if (map != null) {
map.clear();
}
}
}
那么拦截器就可以这样写了
@Override
public boolean preHandle(HttpServletRequest httpServletRequest, HttpServletResponse response, Object handler) throws Exception {
String ID = httpServletRequest.getParameter("ID");
System.out.println("ID:" + ID);
Map<String, String> map = new HashMap<>();
map.put("ID", ID);
//其他信息也可以存储到Map中
UserPool.put(map);
return true;
}
最后接口获取信息就简单了
@RequestMapping(value = "getCode", method = RequestMethod.GET)
public void getCode() {
Map<String,String > map = UserPool.get();
System.out.println("当前登陆ID:"+map.get("ID"));
}
需要注意的是在请求结束的时候我们要清空Map,防止可能会出现的内存泄漏
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, @Nullable Exception ex) throws Exception {
UserPool.remove();
}
关于ThreadLocal可以参考之前写的文章: