✅作者简介:大家好,我是小杨
📃个人主页:「小杨」的csdn博客
🐳希望大家多多支持🥰一起进步呀!
统一用户登录权限验证
1,自定义拦截器
对于统一用户登录权限验证的问题,Spring中提供了具体的解决方法,该方法为设置拦截器:HandlerInterceptor。
自定义拦截器的实现分为以下两个步骤:
-
创建自定义拦截器类,实现HandlerInterceptor接口,并重写preHandle方法,该方法在处理具体方法之前进行预处理。
-
将⾃定义拦截器配置到系统配置项,并且设置合理的拦截规则。实现 WebMvcConfigurer 的 addInterceptors ⽅法。
自定义拦截器创建
// 自定义拦截器创建
public class LoginInterceptor implements HandlerInterceptor {
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
HttpSession session = request.getSession(false);
if(session!=null && session.getAttribute("session_userinfo")!=null){
return true;
}
response.setStatus(401);
return false;
}
}
自定义拦截器创建的实现代码解释:
1,创建自定义拦截器类,实现HandlerInterceptor接口,并重写preHandle方法,该方法在处理具体方法之前进行预处理。
2,preHandle方法的返回结果为t布尔类型的数据,当返回的数据为true时,表示拦截器验证成功,继续执行后续的操作流程,而当返回的结果为false时,表示拦截器验证失败,后续的操作流程就不再执行。
将⾃定义拦截器配置到系统配置项,并且设置合理的拦截规则
@Configuration
public class MyConfiguration implements WebMvcConfigurer {
@Resource
private LoginInterceptor loginInterceptor;
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(loginInterceptor)
.addPathPatterns("/**") // 拦截所有的url
.excludePathPatterns("/login"); // 排除名为login的url
}
}
实现代码解释:
1,创建一个配置类(可使用@Configuration注解),实现WebMvcConfigurer接口,并重写addInterceptors方法。
2,registry
为注册器,通过调用addInterceptor
方法将自定义拦截器添加到系统配置项中。
3,将自定义拦截器添加到系统配置项中有两种写法,分别为:
- 通过new方式添加:registry.addInterceptor(new LoginInterceptor())
- 通过依赖注入方式添加:registry.addInterceptor(loginInterceptor)
4,addPathPatterns
方法设置需要拦截的URL路径,可使用通配符进行匹配;excludePathPatterns
方法设置需要排除的URL路径。
/**
匹配所有的url路径,/*
匹配一级的url路径
2,拦截器的实现原理
正常情况下业务的执行流程:用户或前端程序将请求发送到后端程序的控制器层Controller,经过控制器层Controller的参数校验操作之后,控制器层Controller根据业务代码的逻辑进行调用对应的服务层Service,然后服务层Service进行调用对应的数据持久层Mapper,数据持久层Mapper进而调用相对应的数据库,数据库将对应操作的数据依次返回给数据持久层Mapper,服务层Service,控制器层Controller,最后把数据返回给前端程序或者用户。
正常情况下业务的执行流程如下图所示:
而当添加了拦截器之后,会在调用 Controller 之前进行相应的业务处理,并根据业务处理结果决定后续的操作。
添加了拦截器之后,业务执行的流程如下图所示:
所有的 Controller 执⾏都会通过⼀个调度器 DispatcherServlet 来实现,这⼀点可以从 Spring Boot 控制台的打印信息看出,如下图所示:
而所有的方法都会执行 DispatcherServlet 中的 doDispatch 调度方法,doDispatch 源码如下:
protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {
HttpServletRequest processedRequest = request;
HandlerExecutionChain mappedHandler = null;
boolean multipartRequestParsed = false;
...
// 检查是否为multipart/*类型
processedRequest = checkMultipart(request);
multipartRequestParsed = (processedRequest != request);
if (mappedHandler == null) {
noHandlerFound(processedRequest, response);
return;
}
// 确认支持当前请求Handler的Adapter,用于调用Handler的handle方法
HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());
// 处理last-modified请求头
String method = request.getMethod();
boolean isGet = "GET".equals(method);
if (isGet || "HEAD".equals(method)) {
long lastModified = ha.getLastModified(request, mappedHandler.getHandler());
if (new ServletWebRequest(request, response).checkNotModified(lastModified) && isGet) {
return;
}
}
// 对拦截器链preHandle的调用
if (!mappedHandler.applyPreHandle(processedRequest, response)) {
return;
}
// HandlerAdapter代理执行Handler的handle方法
mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
...
//进行视图的渲染
applyDefaultViewName(processedRequest, mv);
//调用拦截器链的postHandle方法
mappedHandler.applyPostHandle(processedRequest, response, mv);
...
}
从上述源码可以看出在开始执行 Controller 之前,会先调用预处理方法 applyPreHandle。
applyPreHandle 方法的具体实现源码如下:
boolean applyPreHandle(HttpServletRequest request, HttpServletResponse response) throws Exception {
HandlerInterceptor[] interceptors = this.getInterceptors();
if (!ObjectUtils.isEmpty(interceptors)) {
for(int i = 0; i < interceptors.length; this.interceptorIndex = i++) {
//拦截器的preHandle方法返回true的时候顺带记录索引
HandlerInterceptor interceptor = interceptors[i];
if (!interceptor.preHandle(request, response, this.handler)) {
//一旦有个前置处理器没放行, 执行拦截器的afterCompletion方法
this.triggerAfterCompletion(request, response, (Exception)null);
return false;
}
}
}
return true;
}
从上述源码可以看出,在 applyPreHandle 中会获取所有的拦截器 HandlerInterceptor 并执行拦截器中的 preHandle 方法,这样就会咱们前⾯定义的拦截器对应上了。
结语
这就是本期博客的全部内容啦!如果有什么其他的问题无法自己解决,可以在评论区留言哦!
最后,如果你觉得这篇文章写的还不错的话或者有所收获的话,麻烦小伙伴们动动你们的小手,给个三连呗(点赞👍,评论✍,收藏📖),多多支持一下!各位的支持是我最大的动力,后期不断更新优质的内容来帮助大家,一起进步。那我们下期见!