【框架篇】统一用户登录权限验证

✅作者简介:大家好,我是小杨
📃个人主页:「小杨」的csdn博客

🐳希望大家多多支持🥰一起进步呀!


统一用户登录权限验证

1,自定义拦截器

对于统一用户登录权限验证的问题,Spring中提供了具体的解决方法,该方法为设置拦截器:HandlerInterceptor。

自定义拦截器的实现分为以下两个步骤:

  1. 创建自定义拦截器类,实现HandlerInterceptor接口,并重写preHandle方法,该方法在处理具体方法之前进行预处理。

  2. 将⾃定义拦截器配置到系统配置项,并且设置合理的拦截规则。实现 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。

image-20230906141918907

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 方法,这样就会咱们前⾯定义的拦截器对应上了。


结语

这就是本期博客的全部内容啦!如果有什么其他的问题无法自己解决,可以在评论区留言哦!

最后,如果你觉得这篇文章写的还不错的话或者有所收获的话,麻烦小伙伴们动动你们的小手,给个三连呗(点赞👍,评论✍,收藏📖),多多支持一下!各位的支持是我最大的动力,后期不断更新优质的内容来帮助大家,一起进步。那我们下期见!

在这里插入图片描述


  • 33
    点赞
  • 28
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 32
    评论
评论 32
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

小杨MiManchi

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值