【Spring AOP】统一用户登录校验

统一用户登录校验

  • 一. 使用拦截器实现统一用户登录校验
    • 1. 自定义拦截器
    • 2. 将拦截器加入到系统配置
  • 二. 拦截器实现原理
  • 三. 扩展:统一访问前缀添加

一. 使用拦截器实现统一用户登录校验

Spring 中提供了具体的实现拦截器:HandlerInterceptor,拦截器的实现分为以下两个步骤:

  1. 创建⾃定义拦截器,实现 HandlerInterceptor 接⼝的 preHandle(执⾏具体⽅法之前的预处理)⽅法。
  2. 将⾃定义拦截器加⼊ WebMvcConfigurer 的 addInterceptors ⽅法中。

接下来使⽤代码来实现⼀个⽤户登录的权限效验:
具体实现如下:

1. 自定义拦截器

⾃定义拦截器是⼀个普通类, 实现了 HandlerInterceptor 并重写 preHandle 方法。

public class LoginInterceptor implements HandlerInterceptor {@Overridepublic boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {// 执行判断用户是否登录的逻辑HttpSession session = request.getSession(false);// 注意这里面要同时判断 session 和 session 中的某个指定的键值对不为空// 因为后面可能用户登录后又注销了// 此时没有办法把 session 删除, 但是里面的某个指定的键值对可以被删除, 以此来标注用户注销了// 所以判断时要同时判断 session 和 对应的某个键值对if (session != null && session.getAttribute("userinfo") != null) {// 用户已经登录return true;}response.setStatus(401);return false;}
}

2. 将拦截器加入到系统配置

将⾃定义拦截器加⼊到系统配置, 将上⼀步中的⾃定义拦截器加⼊到系统配置信息中:
写一个类实现 WebMvcConfigurer 接口并重写 addInterceptors 方法,不要忘记将这个类注入到容器中

@Configuration // 不要忘记注入到容器中
public class AppConfig implements WebMvcConfigurer { // 添加拦截器@Overridepublic void addInterceptors(InterceptorRegistry registry) {registry.addInterceptor(new LoginInterceptor()).addPathPatterns("/**") // 拦截所有接⼝.excludePathPatterns("/art/param11"); // 排除 /art/param11 这个接⼝}
}
  • addPathPatterns:表示需要拦截的 URL,“**”表示拦截任意⽅法(也就是所有⽅法)。
  • excludePathPatterns:表示需要排除的 URL, 即不拦截的 URL。

以上拦截规则可以拦截此项⽬中的使⽤ URL,包括静态⽂件(图⽚⽂件、JS 和 CSS 等⽂件)。

拦截规则可以根据我们的需要自己设定:

    @Overridepublic void addInterceptors(InterceptorRegistry registry) {registry.addInterceptor(new LoginInterceptor()).addPathPatterns("/**") // 拦截所有接⼝.excludePathPatterns("/**/*.js") // 不拦截对应路径下的 js 文件.excludePathPatterns("/**/*.css") // 不拦截对应路径下的 css 文件.excludePathPatterns("/**/*.jpg") // 不拦截对应路径下的图片.excludePathPatterns("/login.html") // 不拦截登录页面.excludePathPatterns("/**/login"); // 不拦截登录接口}

注意里面的静态文件的路径以及接口都是对应自己存放的文件路径以及名称

一般的拦截规则:

  1. 登录、注册⻚⾯不拦截,其他⻚⾯都拦截。
  2. 当登录成功写⼊ session 之后,拦截的⻚⾯可正常访问。

注意:当我们在项目中新增一些功能时,如果测试时发现代码基本上都没有什么问题,但是就是访问失败,很有可能就是被拦截了,此时我们要将这个功能对应的接口,静态文件给放开,不进行拦截。

二. 拦截器实现原理

在这里插入图片描述
实现原理源码分析:

所有的 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;WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);try {try {ModelAndView mv = null;Object dispatchException = null;try {processedRequest = this.checkMultipart(request);multipartRequestParsed = processedRequest != request;mappedHandler = this.getHandler(processedRequest);if (mappedHandler == null) {this.noHandlerFound(processedRequest, response);return;}HandlerAdapter ha = this.getHandlerAdapter(mappedHandler.getHandler());String method = request.getMethod();boolean isGet = HttpMethod.GET.matches(method);if (isGet || HttpMethod.HEAD.matches(method)) {long lastModified = ha.getLastModified(request, mappedHandler.getHandler());if ((new ServletWebRequest(request, response)).checkNotModified(lastModified) && isGet) {return;}}// 调⽤预处理【重点】if (!mappedHandler.applyPreHandle(processedRequest, response)) {// 如果拦截没通过就不会继续往下执行return;}// 拦截通过则继续往下执行// 执⾏ Controller 中的业务mv = ha.handle(processedRequest, response, mappedHandler.getHandler());if (asyncManager.isConcurrentHandlingStarted()) {return;}this.applyDefaultViewName(processedRequest, mv);mappedHandler.applyPostHandle(processedRequest, response, mv);} catch (Exception var20) {dispatchException = var20;} catch (Throwable var21) {dispatchException = new NestedServletException("Handler dispatch failed", var21);}this.processDispatchResult(processedRequest, response, mappedHandler, mv, (Exception) dispatchException);} catch (Exception var22) {this.triggerAfterCompletion(processedRequest, response, mappedHandler, var22);} catch (Throwable var23) {this.triggerAfterCompletion(processedRequest, response, mappedHandler, new NestedServletException("Handler processing failed", var23));}} finally {if (asyncManager.isConcurrentHandlingStarted()) {if (mappedHandler != null) {mappedHandler.applyAfterConcurrentHandlingStarted(processedRequest, response);}} else if (multipartRequestParsed) {this.cleanupMultipart(processedRequest);}}}

从上述源码可以看出在开始执⾏ Controller 之前,会先调⽤ 预处理⽅法 applyPreHandle,⽽ applyPreHandle ⽅法的实现源码如下:

    boolean applyPreHandle(HttpServletRequest request, HttpServletResponse response) throws Exception {for(int i = 0; i < this.interceptorList.size(); this.interceptorIndex = i++) {// 获取项⽬中使⽤的拦截器 HandlerInterceptor,并执行 preHandle 方法HandlerInterceptor interceptor = (HandlerInterceptor)this.intercep torList.get(i);if (!interceptor.preHandle(request, response, this.handler)) {this.triggerAfterCompletion(request, response, (Exception)null);return false;}}return true;}

从上述源码可以看出,在 applyPreHandle 中会获取所有的拦截器 HandlerInterceptor 并执⾏拦截器中的 preHandle ⽅法,
这样就与咱们前⾯定义的拦截器对应上了,如下图所示:

在这里插入图片描述
此时⽤户登录权限的验证⽅法就会执⾏,这就是拦截器的实现原理。

通过上⾯的源码分析,我们可以看出,Spring 中的拦截器也是通过动态代理和环绕通知的思想实现的,⼤体的调⽤流程如下:

在这里插入图片描述

三. 扩展:统一访问前缀添加

所有请求地址添加 api 前缀:

方法一:

@Configuration
public class AppConfig implements WebMvcConfigurer {// 所有的接⼝添加 api 前缀@Overridepublic void configurePathMatch(PathMatchConfigurer configurer) {configurer.addPathPrefix("/api", c -> true);}
}

其中第一个参数是要加的前缀,
第⼆个参数是⼀个表达式,c -> true 表示一个Lambda表达式,它接受一个条件参数 c,并且无论传入什么条件,它始终返回 true,这就是为什么它表示一个通用条件,即对所有路径都应用前缀设置为 true 表示启动前缀(不包含静态文件资源)。

方法二:直接在配置文件 application.yml (application.properties )中配置

server:servlet:context-path: /api2

举个栗子:
原本访问路径为:

http://localhost:8080/u/getu?id=1

增加访问前缀之后 路径为 :

http://localhost:8080/api/u/getu?id=1

此时再使用之前的访问路径就不能访问到了。

好啦! 以上就是对 统一用户登录校验 的讲解,希望能帮到你 !
评论区欢迎指正 !

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/132293.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

数据结构学习笔记——数据结构概论

目录 一、数据与数据元素二、数据类型和抽象数据类型三、数据结构的定义&#xff08;一&#xff09;逻辑结构&#xff08;二&#xff09;存储结构&#xff08;物理结构&#xff09;1、顺序存储结构2、链式存储结构3、索引存储结构4、散列存储结构 &#xff08;三&#xff09;数…

基于Cl2/BCl3电感偶联等离子体的氮化镓干蚀特性

引言 氮化镓(GaN)具有六方纤锌矿结构&#xff0c;直接带隙约为3.4eV&#xff0c;目前已成为实现蓝光发光二极管(led)的主导材料。由于GaN的高化学稳定性&#xff0c;在室温下用湿法化学蚀刻来蚀刻或图案化GaN是非常困难的。与湿法蚀刻技术相比&#xff0c;干法蚀刻技术可以提供…

【计算机网络】——前言计算机网络发展的历程概述

主页点击直达&#xff1a;个人主页 我的小仓库&#xff1a;代码仓库 C语言偷着笑&#xff1a;C语言专栏 数据结构挨打小记&#xff1a;初阶数据结构专栏 Linux被操作记&#xff1a;Linux专栏 LeetCode刷题掉发记&#xff1a;LeetCode刷题 算法&#xff1a;算法专栏 C头…

wins打开ftp服务,跳转到浏览器解决方式

问题: 在wins的资源管理器中输入 ftp://服务器ip的时候&#xff0c;会突然跳转到浏览器中 百度上的方法归纳汇总 解决方法: 百度上最多的方式&#xff0c;但是我电脑试了不行 启动 InternetExplorer 在 菜单栏 选择 工具 -> Internet 选项 -> 高级 -> 勾选 启用 …

如何查找文献,如何阅读文献

一、高效查找需要阅读的文献 1、首先进入知网的高级检索页&#xff0c;点击“学术期刊”&#xff0c;你会看到“来源类别”选择&#xff0c;在这个里选择北核和C刊 2、在检索结果页选择一篇自己感兴趣的文章&#xff0c;点击篇名进入文章详情页&#xff0c;下拉可看到核心文献…

游戏设计模式专栏(八):Cocos中最常见的设计模式之一

点击上方亿元程序员关注和★星标 引言 大家好&#xff0c;我是亿元程序员&#xff0c;一位有着8年游戏行业经验的主程。 本系列是《和8年游戏主程一起学习设计模式》&#xff0c;让糟糕的代码在潜移默化中升华&#xff0c;欢迎大家关注分享收藏订阅。 组合模式是一种在Cocos…

PreScan与MATLAB联合仿真报错

一、 问题&#xff1a; Error:Matlab ||和&&运算符的操作数必须能够转换为逻辑标量值 二、解决办法 必须安装VS2013&#xff08;我装的VS2017不行的&#xff09;&#xff0c;然后重启prescan和MATLAB&#xff0c;编译通过&#xff0c;界面如下&#xff1a; 三、VS…

【Redis】Hash 哈希内部编码方式

Hash 哈希内部编码方式 哈希的内部编码有两种&#xff1a; ziplist&#xff08;压缩列表&#xff09;&#xff1a;当哈希类型元素个数⼩于hash-max-ziplist-entries配置&#xff08;默认512个&#xff09;、同时所有值都⼩于hash-max-ziplist-value配置&#xff08;默认64字节…

Maven Eclipse

Eclipse 提供了一个很好的插件 m2eclipse &#xff0c;该插件能将 Maven 和 Eclipse 集成在一起。 在最新的 Eclipse 中自带了 Maven&#xff0c;我们打开&#xff0c;Windows->Preferences&#xff0c;如果会出现下面的画面&#xff1a; 下面列出 m2eclipse 的一些特点&a…

腾讯位置服务

1&#xff1a;账号申请 账户申请链接&#xff1a;https://lbs.qq.com/service/webService/webServiceGuide/webServiceOverview

棱镜七彩参编!开源领域4项团体标准正式发布

近日&#xff0c;中电标2023年第27号团体标准公告正式发布&#xff0c;《T/CESA 1270.2-2023 信息技术 开源治理 第 2 部分&#xff1a;企业治理评估模型》、《T/CESA 1270.3-2023 信息技术 开源治理 第 3 部分&#xff1a;社区治理框架》、《T/CESA 1270.5-2023 信息技术 开源…

[天翼杯 2021]esay_eval - RCE(disabled_function绕过||AS_Redis绕过)+反序列化(大小写wakeup绕过)

[天翼杯 2021]esay_eval 1 解题流程1.1 分析1.2 解题1.2.1 一阶段1.2.2 二阶段二、思考总结题目代码: <?php class A{public $code = "";