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

news2025/6/21 9:59:27

统一用户登录校验

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

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

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

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

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

1. 自定义拦截器

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

public class LoginInterceptor implements HandlerInterceptor {
    @Override
    public 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 { // 实现这个接口才能重写方法从而加入到配置文件中,配置文件中的每个类都实现了这个接口
    // 添加拦截器
    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        registry.addInterceptor(new LoginInterceptor())
                .addPathPatterns("/**") // 拦截所有接⼝
                .excludePathPatterns("/art/param11"); // 排除 /art/param11 这个接⼝
    }
}
  • addPathPatterns:表示需要拦截的 URL,“**”表示拦截任意⽅法(也就是所有⽅法)。
  • excludePathPatterns:表示需要排除的 URL, 即不拦截的 URL。

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

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

    @Override
    public 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 前缀
    @Override
    public 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.coloradmin.cn/o/1086667.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【计算机系统】校验码

【计算机系统】校验码 校验码奇偶校验码海明码校验位的位数校验位的位置确定校验的值校验错误检测 循环冗余校验码 校验码 计算机系统运行时&#xff0c;为了确保数据在传送过程中正确无误&#xff0c;一是提高硬件电路的可靠性&#xff0c;二是提高代码的校验能力。通常使用校…

使用telnet+nc工具测试网络连通性

背景&#xff1a; 正常情况下使用ping命令即可测试网络的连通性&#xff0c;但如果做了内网穿透(端口转发)&#xff0c;则需要指定网络端口&#xff0c;此时ping命令无法实现ipport的连通性测试。则可以使用telnetnc测试网络连通性。 环境&#xff1a; 两台服务器都是按照的De…

【Spring AOP】统一异常处理

统一异常处理 统⼀异常处理使⽤的是 ControllerAdvice ExceptionHandler 来实现的&#xff0c; 类上面加上 ControllerAdvice 注解表示控制器通知类方法上面加上 ExceptionHandler 表示异常处理器&#xff0c;并添加异常返回的业务代码 两个结合表示当出现异常的时候执⾏某个…

一文带你简单了解一下堡垒机是干嘛的!

随着国家对网络安全的重视&#xff0c;随着等保政策的落地执行&#xff0c;越来越多的企业知道了堡垒机。但对于堡垒机的作用还不是很了解&#xff0c;很多人在问&#xff0c;堡垒机是干嘛的、这里我们小编就跟大家来简单唠唠。 首先我们来看看什么是堡垒机&#xff1f; 堡垒…

VBA入门3——过程和函数 (Sub | Function)

VBA基础入门2 VBA 过程和函数 (Sub | Function)VBA 过程(Sub) 入门教程和实例&#xff08;组织代码的容器&#xff09;无参数过程有参数过程调用子过程&#xff08;Sub&#xff09;调用子过程和函数的基本语法提前退出过程 VBA 函数(Function)入门教程和实例&#xff08;重复使…

Cookies Session JWT

Cookies Cookies是会话跟踪技术的一种&#xff0c;通过将数据保存到客户端的浏览器上以此来达到会话间共享数据的效果。 从打开浏览器开始访问页面直到关闭浏览器断开连接算作一次会话&#xff08;Session&#xff09;&#xff0c;一次会话中可以包含多次请求-响应。每次的请…

【linux】权限相关问题

【linux】权限相关问题 一.用户的分类sudo 二.文件执行的权限i. 文件的分类ii.人的分类三.修改创建文件的权限chmod更改文件创造的默认权限(umask) 三.删除&#xff08;粘滞位&#xff09; 一.用户的分类 在我们使用linux的时候&#xff0c;有用户类型的区分&#xff0c;不同用…

Linux环境下Qt应用程序打包与发布

本文介绍Linux环境下Qt应用程序的打包与发布。 Linux环境下&#xff0c;在开发机器上开发完应用程序&#xff0c;需要部署到其他非开发环境的机器上&#xff0c;这时&#xff0c;需要对开发的Qt应用程序进行打包&#xff0c;以确保可以在其他机器平台&#xff08;非开发环境&a…

20231012_python练习_服务端与客户端数据交互v2_增加xlsx表格数据批量导入数据库

服务端增加根据上传附件格式 xlsx 类型&#xff0c;将表格第一个sheet数据批量快速导入数据库 服务端 import socketserver import json import os #import pymysql import cx_Oracle #Oracle 数据库连接 import time import tqdm import pandas as pd import openpyxlclass …

Linux---(二)基本认识与安装

文章目录 前言一、操作系统的概念、定位二、使用XShell远程登录Linux1.下载安装XShell2.查看 Linux 主机 ip3.使用XShell登录主机4.XShell 下的复制粘贴 前言 一、操作系统的概念、定位 1.操作系统是一款进行软硬件资源管理的软件 硬件资源管理&#xff1a;将CPU、键盘、显示…

uni-app:本地缓存的使用

uni-app 提供了多种方法用于本地缓存的操作。下面是一些常用的 uni-app 本地缓存方法&#xff1a; uni.setStorageSync(key, data): 同步方式将数据存储到本地缓存中&#xff0c;可以使用对应的 key 来获取该数据。 uni.setStorage({key, data}): 异步方式将数据存储到本地缓存…

Leetcode: 931.下降路径最小和(动态规划)

1. 题目解析 LeetCode链接 根据题目可以得出&#xff0c;当处于 [i][j] 位置时只能从 [i - 1][j - 1]&#xff0c;[i - 1][j]&#xff0c;[i - 1][j 1] 这三个位置到达&#xff0c; 所以我们想要得到当前最小的路径和只需要得到上述三个位置的最小值加上该位置的值即可。 2…

Redis(五)

文章目录 一、Redis键值设计&#xff08;一&#xff09;key结构设计&#xff08;二&#xff09;拒绝BigKey什么是BigKey&#xff1f;BigKey的危害如何发现BigKey &#xff08;一&#xff09;恰当的数据类型 二、批处理优化三、服务端优化四、集群最佳实践 一、Redis键值设计 &…

区块链技术在供应链管理中的创新应用

区块链技术以其独特的不可篡改和透明性特点&#xff0c;正在逐步改变供应链管理的传统模式。本文将探讨区块链技术在供应链管理中的创新应用及其带来的效益。 区块链技术的出现为许多行业带来了创新的可能&#xff0c;其中之一就是供应链管理。通过区块链技术&#xff0c;企业可…

记录项目运行起来后在项目的控制台,直接展示抽中奖品的弹框

首先运行项目或者打开线上项目, 在控制台或者项目代码中找到调用出这个弹框的方法, 在控制台直接vm.这个方法() 来调用 可以看到弹框出来了

MyBatis基础之注解与SQL 语句构建器

文章目录 注解实现简单增删改查SQL 语句构建器SelectProvider举例 注解实现简单增删改查 在 MyBatis 的核心配置文件中&#xff0c;你需要配置的不是 mapper 映射文件&#xff0c;而是 Mapper 接口所在的包路径。 <!-- 在配置文件中 关联包下的 接口类--> <mappers&…

【STM32单片机】宠物定时喂食器设计

文章目录 一、功能简介二、软件设计三、实验现象联系作者 一、功能简介 本项目使用STM32F103C8T6单片机控制器&#xff0c;使用按键、IIC OLED液晶、蜂鸣器、继电器投喂控制等。 主要功能&#xff1a; 系统运行后&#xff0c;OLED液晶显示初始时间&#xff0c;默认工作在自动投…

核心概念:从晶体管到集成电路到摩尔定律

目录 晶体管 集成电路 摩尔定律 总结 这芯片和半导体是不是一回事儿呢&#xff1f;怎么我有时候听人说芯片行业&#xff0c;有时候又听人说半导体行业&#xff0c;好像他们说的都是一回事&#xff1f; 不知道你是不是也有这样的问题。其实啊&#xff0c;这问题很简单&…

arm实验

设置按键中断&#xff0c;按键1按下&#xff0c;LED亮&#xff0c;再次按下&#xff0c;灭 按键2按下&#xff0c;蜂鸣器叫&#xff0c;再次按下&#xff0c;停 按键3按下&#xff0c;风扇转&#xff0c;再次按下&#xff0c;停 头文件 #ifndef __CTRL_KEY_H__ #define __CT…

python 操作FT232H——(1)准备工作与IO输出方波信号

1.准备工作&#xff1a; python环境&#xff1a; 安装python 环境&#xff0c;我目前使用的是pycharm2023.2.1 community Edition 版本 需要注意设置python解释器的版本及关联本地库&#xff0c;这样就不需要在新的工程中安装库文件了&#xff0c;&#xff08;当然也有缺点&…