登录时的校验Token接口开发(Interceptor)

news2025/2/7 3:24:10

// 拦截器校验所有非登录请求时的token,校验成功之后解析出用户信息存入ThreadLocal中便于本次请求中共享该用户的信息,这个信息只能在本线程中拿到

一、需求分析

在用户登录后的请求交互中,Token 的校验是保障用户身份合法性和数据安全的重要环节。通过校验 Token,可以有效防止非法请求、数据篡改等安全问题,同时实现基于用户身份的权限管理。

功能需求
  1. Token 校验逻辑

    • 后端在接收到用户发起的非登录请求时,从请求头中提取 Token。
    • 对 Token 进行解密和验证,确保其合法性(例如签名校验和时间有效性)。
    • 如果 Token 不合法(为空或解密失败),则返回 401 错误,提示用户重新登录。
    • 如果 Token 合法,从中解析出用户的身份信息(如用户 ID 和用户名)。
  2. 用户身份获取

    • 将解析出的用户身份信息存储到当前线程的 ThreadLocal 中,便于后续业务层和控制层直接调用,避免多次重复解析。
  3. 业务请求支持

    • 在业务层中,能够通过校验后的用户身份信息,执行相应的操作(如查询用户专属数据、验证用户权限等)。

流程需求
  1. 登录阶段

    • 用户登录成功后,后端生成 JWT Token 并返回给前端。
    • 前端将 Token 存储在本地(如 localStoragesessionStorage),并在后续请求中携带该 Token。
  2. 校验阶段

    • 在后端,通过拦截器(Interceptor)拦截非登录请求,提取请求头中的 Token。
    • 对 Token 进行解析和校验,确保用户身份的合法性。
    • 校验成功后,将用户身份信息存储到 ThreadLocal,供业务逻辑调用。
  3. 响应阶段

    • 如果校验失败,返回错误信息(如 401 未授权)。
    • 如果校验成功,继续执行对应的业务逻辑。

二、代码解读

拦截器类:UserInterceptor

功能:实现 HandlerInterceptor 接口,对所有请求进行拦截,校验 Token,解析用户身份并存储到 ThreadLocal 中。

方法 preHandle

@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
    // 如果不是映射到方法就直接放行
    if (!(handler instanceof HandlerMethod)) {
        return true;
    }

    // 获取请求头中的 Token
    String token = request.getHeader(Constants.USER_TOKEN);
    log.info("开始解析 customer user token:{}", token);

    // Token 为空或无效时,抛出异常
    if (ObjectUtil.isEmpty(token)) {
        throw new BaseException(BasicEnum.SECURITY_ACCESSDENIED_FAIL);
    }

    // 解析 Token 并获取载荷信息
    Map<String, Object> claims = JwtUtil.parseJWT(jwtTokenManagerProperties.getBase64EncodedSecretKey(), token);
    if (ObjectUtil.isEmpty(claims)) {
        throw new BaseException(BasicEnum.SECURITY_ACCESSDENIED_FAIL);
    }

    // 获取用户 ID
    Long userId = MapUtil.get(claims, Constants.JWT_USERID, Long.class);
    if (ObjectUtil.isEmpty(userId)) {
        throw new BaseException(BasicEnum.SECURITY_ACCESSDENIED_FAIL);
    }

    // 将用户 ID 存入当前线程中
    UserThreadLocal.set(userId);

    // 返回 true 允许请求继续执行
    return true;
}
  • 逻辑说明

    • 判断当前请求是否映射到方法(HandlerMethod)。如果不是(例如静态资源或跨域请求),直接放行。
    • 从请求头中获取 Token(authorization),并检查其是否为空。
    • 通过 JwtUtil.parseJWT 方法解析 Token,校验其合法性。
    • 从解析后的载荷中提取用户 ID,并存入 ThreadLocal,供后续业务逻辑调用。
    • 如果校验通过,返回 true,请求继续执行;否则抛出异常。
  • 安全保障

    • Token 为空、无效或解析失败时,都会抛出异常,阻止非法请求进入业务逻辑层。
    • 使用 ThreadLocal 确保当前线程独立存储用户身份信息,避免多线程访问数据冲突。
  • 异常处理

    • 抛出自定义异常 BaseException,状态码和错误信息由 BasicEnum 枚举定义。
    • 典型错误场景包括:Token 缺失、Token 解析失败、用户 ID 为空等。

方法 afterCompletion

@Override
public void afterCompletion(HttpServletRequest request, 
HttpServletResponse response, Object handler, Exception ex) 
throws Exception {
    // 响应结束时清理 ThreadLocal 中的数据,防止内存泄漏
    UserThreadLocal.remove();
}
  • 逻辑说明

    • 每次请求完成后,都会清理 ThreadLocal 中存储的用户信息,避免数据在线程间污染。
    • 此方法是 Spring 的拦截器生命周期方法之一,在请求完成后被自动调用。
  • 目的

    • 防止内存泄漏和线程间数据污染,确保系统运行的稳定性和安全性。

常量类:Constants

public static final String USER_TOKEN = "authorization";

  • 定义了请求头中携带 Token 的字段名 authorization
  • 所有请求都会从请求头中提取该字段的值进行校验。

Web MVC 配置类:WebMvcConfig

  • 功能:通过 Spring 的拦截器注册机制,将 UserInterceptor 配置为全局拦截器,并对某些接口进行过滤。

拦截器注册

@Override
public void addInterceptors(InterceptorRegistry registry) {
    // 注册拦截器
    registry.addInterceptor(userInterceptor)
        // 排除不需要拦截的路径(如 Swagger 文档、登录接口等)
        .excludePathPatterns(EXCLUDE_PATH_PATTERNS)
        // 指定拦截的路径
        .addPathPatterns("/customer/**");
}
  • addInterceptor

    • 将自定义拦截器 userInterceptor 注册到拦截器链中,作用于指定的请求路径。
  • excludePathPatterns

    • 定义了拦截器的排除路径,以下场景不会触发拦截:
      • Swagger 相关路径:/swagger-ui.html/v2/api-docs 等。
      • 登录接口:/customer/user/login,无需校验 Token。
  • addPathPatterns

    • 定义需要拦截的路径,这里拦截所有以 /customer/** 开头的接口请求。

UserThreadLocal



/**
 * subjectContent.java
 *  用户主体对象
 */
@Slf4j
public class UserThreadLocal {


    /***
     *  创建线程局部userVO变量
     */
    public static ThreadLocal<String> subjectThreadLocal = new ThreadLocal<String>() {
        @Override
        protected String initialValue() {
            return null;
        }
    };


    // 提供线程局部变量set方法
    public static void setSubject(String subject) {

        subjectThreadLocal.set(subject);
    }
    // 提供线程局部变量get方法
    public static String getSubject() {

        return subjectThreadLocal.get();
    }

    //清空当前线程,防止内存溢出
    public static void removeSubject() {

        subjectThreadLocal.remove();
    }
    private static final ThreadLocal<Long> LOCAL = new ThreadLocal<>();

    private UserThreadLocal() {

    }

    /**
     * 将authUserInfo放到ThreadLocal中
     *
     * @param authUserInfo {@link Long}
     */
    public static void set(Long authUserInfo) {
        LOCAL.set(authUserInfo);
    }

    /**
     * 从ThreadLocal中获取authUserInfo
     */
    public static Long get() {
        return LOCAL.get();
    }

    /**
     * 从当前线程中删除authUserInfo
     */
    public static void remove() {
        LOCAL.remove();
    }

    /**
     * 从当前线程中获取前端用户id
     * @return 用户id
     */
    public static Long getUserId() {
        return LOCAL.get();
    }

    /**
     * 从当前线程中获取前端后端id
     * @return 用户id
     */
    public static Long getMgtUserId() {
        String subject = subjectThreadLocal.get();
        if (ObjectUtil.isEmpty(subject)) {
            return null;
        }
        BaseVo baseVo = JSONObject.parseObject(subject, BaseVo.class);
        return baseVo.getId() ;
    }


}
1. setSubject(String subject)
  • 功能:将用户主体信息(字符串)存储到当前线程的 subjectThreadLocal 中。
  • 使用场景:解析用户主体信息后调用,便于后续获取。
2. getSubject()
  • 功能:从当前线程的 subjectThreadLocal 中获取用户主体信息。
  • 使用场景:需要访问当前线程用户主体信息时调用。
3.removeSubject()
  • 功能:清理当前线程的 subjectThreadLocal,防止内存泄漏。
  • 使用场景:请求结束时调用,确保线程数据不被污染。

三、总结

  • 校验 Token

    • 后端通过 UserInterceptor 拦截请求,从请求头中提取 Token。
    • 使用工具类 JwtUtil 解析 Token,验证其合法性、有效性。
    • 将解析后的用户身份信息存入 ThreadLocal,供业务层使用。
  • ThreadLocal 的应用

    • 使用 UserThreadLocal 工具类隔离多线程环境下的用户数据,避免数据冲突。
    • 在请求完成后,清理 ThreadLocal 中的用户信息,防止内存泄漏。
  • 拦截器的灵活性

    • 配置了拦截路径与排除路径,确保仅对需要校验的接口进行拦截(例如,登录接口不需要校验 Token)。

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

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

相关文章

Hyper-V如何将文件复制到虚拟机

创建Hyper-V共享文件夹 通过创建共享文件夹的方式&#xff0c;能够帮助我们在主机与虚拟机之间轻松地进行数据交换共享&#xff0c;那么具体该如何通过网络共享来将文件复制到虚拟机呢&#xff1f;请您接着往下看。 步骤1. 在Hyper-V主机中创建一个文件夹&#xff0c;并将您想…

Linux(Centos 7.6)yum源配置

yum是rpm包的管理工具&#xff0c;可以自动安装、升级、删除软件包的功能&#xff0c;可以自动解决软件包之间的依赖关系&#xff0c;使得用户更方便软件包的管理。要使用yum必须要进行配置&#xff0c;个人将其分为三类&#xff0c;本地yum源、局域网yum源、第三方yum源&#…

go语言中zero框架项目日志收集与配置

在 GoZero 项目中&#xff0c;日志收集和配置是非常重要的&#xff0c;尤其是在分布式系统中&#xff0c;日志可以帮助开发人员追踪和排查问题。GoZero 提供了灵活的日志系统&#xff0c;能够方便地进行日志的配置和管理。 以下是如何在 GoZero 项目中进行日志收集与配置的基本…

第4章 共享内存范式:基于R(Rdsm)的简单介绍

第4章 4.1 是什么被共享了&#xff1f; 4.1.1 全局变量 4.1.2 局部变量&#xff1a;栈结构 4.3 共享内存编程的高级介绍&#xff1a;Rdsm包 4.3.1 使用共享内存 4.4 示例&#xff1a;矩阵乘法 4.4.1 代码 4.4.2 分析 4.4.3 代码 4.4.4 详解我们数据的共享本质 4.4.5 计时…

optuna和 lightgbm

文章目录 optuna使用1.导入相关包2.定义模型可选参数3.定义训练代码和评估代码4.定义目标函数5.运行程序6.可视化7.超参数的重要性8.查看相关信息9.可视化的一个完整示例10.lightgbm实验 optuna使用 1.导入相关包 import torch import torch.nn as nn import torch.nn.functi…

SD ComfyUI工作流 对人物图像进行抠图并替换背景

文章目录 人物抠图与换背景SD模型Node节点工作流程工作流下载效果展示人物抠图与换背景 此工作流旨在通过深度学习模型完成精确的人物抠图及背景替换操作。整个流程包括图像加载、遮罩生成、抠图处理、背景替换以及最终的图像优化。其核心基于 SAM(Segment Anything Model)与…

【C语言程序设计——循环程序设计】利用循环求数值 x 的平方根(头歌实践教学平台习题)【合集】

目录&#x1f60b; 任务描述 相关知识 一、求平方根的迭代公式 1. 原理 2. 代码实现示例 二、绝对值函数fabs() 1. 函数介绍 2. 代码示例 三、循环语句 1. for循环 2. while循环 3. do - while循环 编程要求 测试说明 通关代码 测试结果 任务描述 本关任务&…

程序猿成长之路之设计模式篇——结构型设计模式

本篇开始介绍结构型设计模式 前言 与创建型设计模式用于创建对象不同&#xff0c;结构型设计模式通过结构化的方式实现功能的扩展和解耦&#xff0c;通过对象的组合、聚合、继承和接口等机制来定义对象之间的关系&#xff0c;从而实现松耦合和灵活性。 常见的结构性设计模式&…

低代码开源项目Joget的研究——Joget8社区版安装部署

大纲 环境准备安装必要软件配置Java配置JAVA_HOME配置Java软链安装三方库 获取源码配置MySql数据库创建用户创建数据库导入初始数据 配置数据库连接配置sessionFactory&#xff08;非必须&#xff0c;如果后续保存再配置&#xff09;编译下载tomcat启动下载aspectjweaver移动jw…

数据库的概念和操作

目录 1、数据库的概念和操作 1.1 物理数据库 1. SQL SERVER 2014的三种文件类型 2. 数据库文件组 1.2 逻辑数据库 2、数据库的操作 2.1 T-SQL的语法格式 2.2 创建数据库 2.3 修改数据库 2.4 删除数据库 3、数据库的附加和分离 1、数据库的概念和操作 1.1 物理数据库…

React中最优雅的异步请求

给大家分享在React19中使用useSuspense处理异步请求为什么是被认为最优雅的解决方案 一. 传统方案 解决异步请求的方案中&#xff0c;我们要处理至少两个最基本的逻辑 正常的数据显示数据加载的UI状态 例如&#xff1a; export default function Index(){const [content, …

基于Bregman的交替方向乘子法

目录标题 ADMM方法简介Bregman散度Bregman ADMM的原理主要优势代码示例&#xff1a;各个符号的解释&#xff1a;**梯度的几何含义**&#xff1a;具体数学公式&#xff1a;**应用示例**&#xff1a;**ADMM的标准形式&#xff1a;****ADMM中的变量角色&#xff1a;****ADMM中的更…

`we_chat_union_id IS NOT NULL` 和 `we_chat_union_id != ‘‘` 这两个条件之间的区别

文章目录 1、什么是空字符串&#xff1f;2、两个引号之间加上空格 好的&#xff0c;我们来详细解释一下 we_chat_union_id IS NOT NULL 和 we_chat_union_id ! 这两个条件之间的区别&#xff0c;以及它们在 SQL 查询中的作用&#xff1a; 1. we_chat_union_id IS NOT NULL 含…

随机变量是一个函数-如何理解

文章目录 一. 随机变量二. 随机变量是一个函数-栗子(一对一)1. 掷骰子的随机变量2. 掷骰子的随机变量&#xff08;求点数平方&#xff09;3. 抛硬币的随机变量4. 学生考试得分的随机变量 三. 随机变量是一个函数-理解(多对一) 一. 随机变量 随机变量就是定义在样本空间上的函数…

jwt在express中token的加密解密实现方法

在我们前面学习了 JWT认证机制在Node.js中的详细阐述 之后&#xff0c;今天来详细学习一下token是如何生成的&#xff0c;secret密钥的加密解密过程是怎么样的。 安装依赖 express&#xff1a;用于创建服务器jsonwebtoken&#xff1a;用于生成和验证JWTbody-parser&#xff1…

大厂开发规范-如何规范的提交Git

多人协作开发提交代码通常是遵循约定式提交规范&#xff0c;如果严格安照约定式提交规范&#xff0c; 手动进行代码提交的话&#xff0c;那么是一件非常痛苦的事情&#xff0c;但是 Git 提交规范的处理又势在必行&#xff0c;那么怎么办呢&#xff1f; 经过了很多人的冥思苦想…

企业安装加密软件有什么好处?

加密软件为企业的安全提供了很多便利&#xff0c;从以下几点我们看看比较重要的几个优点&#xff1a; 1、数据保护&#xff1a;企业通常拥有大量的商业机密、客户数据、技术文档等敏感信息。加密软件可以对这些信息进行加密处理&#xff0c;防止未经授权的人员访问。即使数据被…

【ANGULAR网站开发】初始环境搭建

1. 初始化angular项目 1.1 创建angular项目 需要安装npm和nodejs&#xff0c;这边不在重新安装 直接安装最新版本的angular npm install -g angular/cli安装指定大版本的angular npm install -g angular/cli181.2 启动angular 使用idea启动 控制台启动 ng serve启动成功…

Python 屏幕取色工具

Python 屏幕取色工具 1.简介&#xff1a; 屏幕取色小工具‌是一种实用的软件工具&#xff0c;主要用于从屏幕上精确获取颜色值&#xff0c;非常适合设计、编程等需要精确配色的领域。这类工具通常能够从屏幕上任何区域精确提取颜色值&#xff0c;支持在整数值、RGB值、BGR值之…

宏集eX710物联网工控屏在石油开采机械中的应用与优势

案例概况 客户&#xff1a;天津某石油机械公司 应用产品&#xff1a;宏集eX710物联网工控屏 应用场景&#xff1a;钻井平台设备控制系统 一、应用背景 石油开采和生产过程复杂&#xff0c;涵盖钻井平台、采油设备、压缩机、分离器、管道输送系统等多种机械设备。这些设备通…