SpringMVC执行流程
- 用户发起请求到dispatchServlet
- dispatchServlet根据请求路径到HandleMapping(映射器)中查询具体的handler(处理器)
- handlerMapping返回handleExecutionchain(具体的handler和拦截器集合)
- dispatchServlet根据handler的实现方式调用对应的handlerAdapter(适配器)
- handlerAdapter调用具体的handler(处理业务逻辑)
- handler返回处理结果(modelandview:数据模型和视图名称)给handlerAdapter
- 返回modelandview给dispatchservlet
- dispatchservlert根据视图名称到viewresolver(视图解析器)
- 返回具体的视图给dispatcherservlet
- 渲染视图
- 返回渲染后的视图给dispatcherservlet
- dispatcherservlet响应用户请求
自定义拦截器
此拦截器的功能为:拦截用户请求,从请求中获取cookie中的token,对token进行解析,获取token中用户的信息,对获得的信息通过ThreadLocal进行传递,保证线程安全,并在拦截器的后置处理中释放线程
1.编写拦截器类,继承HandlerInterceptorAdapter类,重写preHandle(),并加入到Spring容器中
@Component
public class LoginInterceptor extends HandlerInterceptorAdapter {
private static final ThreadLocal<UserInfo> THREAD_LOCAL = new ThreadLocal<>();
@Autowired
private JwtProperties jwtProperties;
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
// 获取cookie信息
String token = CookieUtils.getCookieValue(request, jwtProperties.getCookieName());
String userKey = CookieUtils.getCookieValue(request, jwtProperties.getUserKeyName());
// 如果都为空,设置userKey。
if (StringUtils.isBlank(token) && StringUtils.isBlank(userKey)){
userKey = UUID.randomUUID().toString();
CookieUtils.setCookie(request, response, jwtProperties.getUserKeyName(), userKey, jwtProperties.getExpire());
}
// 不管有没有登录都要设置userKey
UserInfo userInfo = new UserInfo();
userInfo.setUserKey(userKey);
// token不为空,解析token
if (StringUtils.isNotBlank(token)){
Map<String, Object> map = JwtUtils.getInfoFromToken(token, jwtProperties.getPublicKey());
if (!CollectionUtils.isEmpty(map)) {
userInfo.setId(new Long(map.get("id").toString()));
}
}
// 保存到threadlocal
THREAD_LOCAL.set(userInfo);
// 如果token不为空,
return true;
}
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
THREAD_LOCAL.remove();
}
//对外提供静态方法,获取ThreadLocal
public static UserInfo getUserInfo(){
return THREAD_LOCAL.get();
}
}
注意:
- 这里我们使用了
ThreadLocal
来存储查询到的用户信息,线程内共享,对外提供get方法,因此请求到达Controller
后可以共享User - 并且对外提供了静态的方法:
getUserInfo()
来获取User信息 - 在完成逻辑任务后,要进行释放,否则会内存泄漏
2.配置拦截器
@Configuration
public class MvcConfig implements WebMvcConfigurer {
@Autowired
private LoginInterceptor loginInterceptor;
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(loginInterceptor).addPathPatterns("/**");
}
}