【技术派后端篇】技术派中 Session/Cookie 与 JWT 身份验证技术的应用及实现解析
在现代Web应用开发中,身份验证是保障系统安全的重要环节。技术派在身份验证领域采用了多种技术方案,其中Session/Cookie和JWT(JSON Web Token)是两种常用的实现方式。本文将详细介绍这两种身份验证技术在技术派中的应用及具体实现。
1 Session/Cookie身份验证
1.1 基本原理
技术派的用户登录信息主要通过Session/Cookie机制实现。其核心原理是利用Cookie中的JESSIONID作为用户身份标识,若JESSIONID相同,则视为同一用户。服务器会在内存中存储Session数据,并设置过期时间,通常每次用户访问时都会刷新该过期时间。当浏览器不支持Cookie时,可通过URL重写的方式,将sessionId写入URL地址中,参数名为jsessionid。

1.2 在SpringBoot项目中的实现
项目仓库(GitHub):https://github.com/itwanger/paicoding
项目仓库(码云):https://gitee.com/itwanger/paicoding
1.2.1 登录入口,保存Session
在SpringBoot项目中,通过以下代码实现登录接口并保存Session:
@RestController
public class SessionController {@RequestMapping(path = "/login")public String login(String uname, HttpSession httpSession) {httpSession.setAttribute("name", uname);return "欢迎登录:" + uname;}
}
上述代码中,在login方法中通过HttpSession对象将用户名存储到Session中,后续在会话期间,其他请求可获取该Session信息。
1.2.2 Session读取测试
提供两种常见的Session获取方式:
- 直接从
HttpSession中获取:
@RestController
public class SessionController {@RequestMapping("time")public String showTime(HttpSession session) {return session.getAttribute("name") + " ,当前时间为:" + LocalDateTime.now();}
}
- 通过
HttpServletRequest来获取:
@RestController
public class SessionController {@RequestMapping("name")public String showName(HttpServletRequest request) {return "当前登录用户:" + request.getSession().getAttribute("name");}
}
1.2.3 退出登录
退出登录的实现代码如下:
@RestController
public class SessionController {@RequestMapping("logout")public String logOut(HttpServletResponse response) throws IOException {// 这里假设通过某个上下文获取当前请求的Session信息// 实际应用中根据具体的上下文获取方式进行调整Optional.ofNullable(ReqInfoContext.getReqInfo()).ifPresent(s -> {// 执行登出逻辑,如清除Session相关数据sessionService.logout(s.getSession());});// 重定向到首页response.sendRedirect("/");return "已成功登出";}
}
1.2.4 Session实现原理
SpringBoot的Session机制工作原理如下:
- 借助Cookie中的
JESSIONID来识别用户身份,将Session存储在内存中,并设置过期时间,每次访问会刷新过期时间。 - 当浏览器关闭后重新打开,会重新生成
JESSIONID的Cookie值,导致服务器无法识别之前的用户。
1.3 技术派的身份认证流程(以管理员后台为例)
整个流程如下图所示:

1.3.1 登录与登出
在技术派的管理员后台登录实现中,相关代码位于AdminLoginController类:
@RestController
@RequestMapping(path = {"/api/admin/login", "/admin/login"})
public class AdminLoginController {private final UserService userService;private final SessionService sessionService;public AdminLoginController(UserService userService, SessionService sessionService) {this.userService = userService;this.sessionService = sessionService;}@PostMapping(path = {"", "/"})public ResVo<BaseUserInfoDTO> login(HttpServletRequest request, HttpServletResponse response) {String user = request.getParameter("username");String pwd = request.getParameter("password");BaseUserInfoDTO info = userService.passwordLogin(user, pwd);String session = sessionService.login(info.getUserId());if (session != null &&!session.isEmpty()) {// 将Session信息写入Cookieresponse.addCookie(new Cookie(SessionService.SESSION_KEY, session));return ResVo.ok(info);} else {return ResVo.fail(StatusEnum.LOGIN_FAILED_MIXED, "登录失败,请重试");}}@RequestMapping("logout")public ResVo<Boolean> logOut(HttpServletResponse response) throws IOException {Optional.ofNullable(ReqInfoContext.getReqInfo()).ifPresent(s -> sessionService.logout(s.getSession()));// 重定向到首页response.sendRedirect("/");return ResVo.ok(true);}
}
上述代码中,登录时先验证用户密码,生成唯一的Session值并保存到Redis缓存,然后将Session写入Cookie返回给前端;登出时清除相关Session数据并重定向。
1.3.2 用户身份识别
用户身份识别的核心逻辑位于ReqRecordFilter中。为了更好地实现功能解耦,建议将用户身份识别功能分离到独立的Filter中,原ReqRecordFilter仅负责请求日志记录。

/*** 初始化用户信息** @param reqInfo*/public void initLoginUser(ReqInfoContext.ReqInfo reqInfo) {HttpServletRequest request =((ServletRequestAttributes) RequestContextHolder.currentRequestAttributes()).getRequest();if (request.getCookies() == null) {return;}Optional.ofNullable(SessionUtil.findCookieByName(request, LoginService.SESSION_KEY)).ifPresent(cookie -> initLoginUser(cookie.getValue(), reqInfo));}public void initLoginUser(String session, ReqInfoContext.ReqInfo reqInfo) {BaseUserInfoDTO user = userService.getAndUpdateUserIpInfoBySessionId(session, null);reqInfo.setSession(session);if (user != null) {reqInfo.setUserId(user.getUserId());reqInfo.setUser(user);reqInfo.setMsgNum(notifyService.queryUserNotifyMsgCount(user.getUserId()));}}
- 获取当前请求对象:借助
RequestContextHolder来获取当前的HttpServletRequest对象,这个对象可用于访问请求相关的信息,像请求头、请求参数、Cookie 等。 - 检查 Cookie 是否存在:查看请求中的 Cookie 是否为空,若为空则直接返回,不进行后续操作。
- 查找指定 Cookie 并初始化用户信息:运用
SessionUtil.findCookieByName方法查找名为LoginService.SESSION_KEY的Cookie,若找到该 Cookie,就调用initLoginUser方法,传入 Cookie 的值和 ReqInfo 对象来初始化登录用户信息。 initLoginUser(String session, ReqInfoContext.ReqInfo reqInfo)方法会依据传入的 session 值从数据库获取用户信息,并且更新用户的 IP 信息,之后将用户信息、会话 ID 和消息数量设置到 ReqInfo 对象里。
综上所述,initLoginUser(ReqInfoContext.ReqInfo reqInfo) 方法的核心功能是从请求的 Cookie 里获取会话信息,然后初始化登录用户的相关信息并存储到 ReqInfo 对象中。
1.4 优缺点
- 优点:实现相对简单,符合传统Web开发的会话管理习惯,适用于对会话管理要求较高的场景。
- 缺点:
- Session存储在内存中,受内存大小限制,可能导致内存溢出(OOM)问题。
- 浏览器关闭再打开后,无法识别之前的用户会话。
- Cookie若被窃取,用户身份存在安全风险。
2 JWT身份验证
2.1 基本原理
JWT(JSON Web Token)是一种用于在网络应用间安全传输信息的开放标准(RFC 7519)。它由三部分组成:Header(头部)、Payload(负载)和Signature(签名)。
-
Header
-
包含令牌类型(如JWT)和签名算法(如HMAC SHA256或RSA)。
-
示例
{"alg": "HS256","typ": "JWT" } -
经Base64编码后生成JWT第一部分。
-
-
Payload
-
存储用户声明(Claims),包括三类:
- Registered Claims:预定义字段(如iss签发者、exp过期时间)。
- Public Claims:自定义公开字段。
- Private Claims:业务相关私有字段。
-
示例
{"iss": "一灰灰blog","exp": 1692256049,"uname": "一灰","wechat": "https://spring.hhui.top/spring-blog/imgs/info/wx.jpg" } -
经Base64编码后生成JWT第二部分。
-
-
Signature
- 对编码后的Header和Payload,使用密钥和指定算法(如HMAC SHA256)生成签名,防止数据篡改。
- 生成公式示例:
HMACSHA256(base64UrlEncode(header) + "." + base64UrlEncode(payload), secret)
2.2 在技术派中的使用姿势
2.2.1 JWT鉴权流程

上图展示了基于JWT(JSON Web Token)的用户身份验证和请求处理流程,具体如下:
- 登录请求
前端用户输入用户名和密码,发起登录请求,将用户名和密码作为参数发送给后端。这是流程的起始点,目的是让后端验证用户身份 。 - 生成并返回JWT
后端接收到登录请求后,对用户名和密码进行验证。如果验证通过,后端生成一个JWT ,并将其返回给前端。JWT包含了用户的身份信息等内容,用于后续请求的身份验证 。 - 前端发起请求并携带JWT
前端接收到JWT后,将其保存到本地(如localStorage、sessionStorage等 )。之后前端向后端发起其他业务请求时,会从本地获取JWT,并将其携带在请求头中(常见是放在Authorization头中,格式为Bearer <JWT>),发送给后端 。 - 后端校验JWT并响应
- 校验失败:后端接收到请求后,从请求头中获取JWT,并进行校验(包括签名验证、有效期检查等 )。如果校验失败,说明用户未登录或JWT无效,后端返回未登录提示,或重定向到登录页面,让用户重新登录 。
- 校验通过:如果JWT校验通过,表明用户身份合法,后端正常处理请求,并返回请求结果给前端 。
2.2.2 实现代码
技术派中JTW的核心逻辑代码位于com.github.paicoding.forum.service.user.service.help.UserSessionHelper类。
UserSessionHelper 是一个用于处理用户会话管理的工具类,借助 JWT(JSON Web Token)来存储用户的会话信息,同时结合 Redis 实现会话的主动失效功能。下面详细阐述其代码逻辑:
- 类和依赖注入
@Slf4j @Component public class UserSessionHelper {@Component@Data@ConfigurationProperties("paicoding.jwt")public static class JwtProperties {private String issuer;private String secret;private Long expire;}private final JwtProperties jwtProperties;private Algorithm algorithm;private JWTVerifier verifier;
-
@Slf4j:使用 Lombok 注解,为类添加日志记录功能。 -
@Component:将UserSessionHelper类注册为 Spring 组件,使其能被 Spring 容器管理。 -
JwtProperties:静态内部类,借助@ConfigurationProperties注解从配置文件里读取paicoding.jwt前缀的配置信息,包含签发人 issuer、密钥 secret 和有效期 expire。

-
algorithm:JWT 签名算法,使用 HMAC256 算法。 -
verifier:JWT 验证器,用于验证 JWT 的合法性。
- 构造函数
public UserSessionHelper(JwtProperties jwtProperties) {this.jwtProperties = jwtProperties;algorithm = Algorithm.HMAC256(jwtProperties.getSecret());verifier = JWT.require(algorithm).withIssuer(jwtProperties.getIssuer()).build(); }
- 构造函数接收
JwtProperties对象,初始化签名算法和验证器。
- 生成会话方法
genSessionpublic String genSession(Long userId) {// 1.生成jwt格式的会话,内部持有有效期,用户信息String session = JsonUtil.toStr(MapUtils.create("s", SelfTraceIdGenerator.generate(), "u", userId));String token = JWT.create().withIssuer(jwtProperties.getIssuer()).withExpiresAt(new Date(System.currentTimeMillis() + jwtProperties.getExpire())).withPayload(session).sign(algorithm);// 2.使用jwt生成的token时,后端可以不存储这个session信息, 完全依赖jwt的信息// 但是需要考虑到用户登出,需要主动失效这个token,而jwt本身无状态,所以再这里的redis做一个简单的token -> userId的缓存,用于双重判定RedisClient.setStrWithExpire(token, String.valueOf(userId), jwtProperties.getExpire() / 1000);return token; }
- 生成包含用户信息的 JSON 字符串
session。 - 利用 JWT 创建一个包含签发人、过期时间和有效载荷的 token。
- 将生成的 token 和对应的用户 ID 存储到 Redis 中,并设置过期时间。
- 移除会话方法
removeSessionpublic void removeSession(String session) {RedisClient.del(session); }
- 从 Redis 中删除指定的会话信息,实现会话的主动失效。
-
根据会话获取用户信息方法
getUserIdBySessionpublic Long getUserIdBySession(String session) {// jwt的校验方式,如果token非法或者过期,则直接验签失败try {DecodedJWT decodedJWT = verifier.verify(session);String pay = new String(Base64Utils.decodeFromString(decodedJWT.getPayload()));// jwt验证通过,获取对应的userIdString userId = String.valueOf(JsonUtil.toObj(pay, HashMap.class).get("u"));// 从redis中获取userId,解决用户登出,后台失效jwt token的问题String user = RedisClient.getStr(session);if (user == null || !Objects.equals(userId, user)) {return null;}return Long.valueOf(user);} catch (Exception e) {log.info("jwt token校验失败! token: {}, msg: {}", session, e.getMessage());return null;} }
- 使用
verifier验证 JWT 的合法性,若验证失败则捕获异常并记录日志。 - 解码 JWT 的有效载荷,从中提取用户 ID。
- 从 Redis 中获取对应的用户 ID,对比两者是否一致,若不一致则返回
null。
综上所述,UserSessionHelper 类结合 JWT 和 Redis 实现了用户会话的生成、管理和验证功能,既利用了 JWT 的无状态特性,又通过 Redis 解决了 JWT 无法主动失效的问题。
2.3 优缺点
- 优点:
- 自包含性:令牌中包含用户信息,无需在服务端存储会话状态,便于在分布式系统中使用。
- 跨域友好:可通过多种方式传输,如URL、HTTP Header或Cookie,适用于不同域之间的身份验证。
- 安全性较高:通过签名机制保证令牌的完整性和真实性。
- 缺点:
- 令牌体积较大:包含用户信息和签名等内容,可能增加传输数据量。
- 无法主动失效:一旦生成,在有效期内无法主动使其失效,除非采用额外机制(如黑名单)。
3 总结
Session/Cookie和JWT身份验证各有优劣。Session/Cookie适用于传统单体应用,对会话管理要求较高且信任浏览器端Cookie的场景;JWT则更适合分布式系统、前后端分离架构以及对无状态性要求较高的场景。技术派在实际项目开发中,应根据具体需求、系统架构和安全要求,合理选择或结合使用这两种身份验证方式,以构建安全可靠的用户身份验证体系。
4 参考链接
- 技术派Session/Cookie身份验证
- 技术派JWT身份验证
相关文章:
【技术派后端篇】技术派中 Session/Cookie 与 JWT 身份验证技术的应用及实现解析
在现代Web应用开发中,身份验证是保障系统安全的重要环节。技术派在身份验证领域采用了多种技术方案,其中Session/Cookie和JWT(JSON Web Token)是两种常用的实现方式。本文将详细介绍这两种身份验证技术在技术派中的应用及具体实现…...
【基础】Node.js 介绍、安装及npm 和 npx功能了解
前言 后面安装n8n要用到,做一点技术储备。主要是它的两个工具:npm 和 npx。 Node.js介绍 Node.js 是一个免费的、开源的、跨平台的 JavaScript 运行时环境,允许开发人员在浏览器之外编写命令行工具和服务器端脚本,是一个基于 C…...
第53讲 农学科研中的AI伦理与可解释性——探索SHAP值、LIME等可解释工具与科研可信性建设之道
目录 一、为什么农学科研中需要“可解释AI”? ✅ 场景示例: 二、常见可解释AI工具介绍 1. SHAP(SHapley Additive exPlanations) 2. LIME(Local Interpretable Model-agnostic Explanations) 三、AI伦理问题在农学中的体现 🧭 公平性与偏见 🔐 数据隐私 🤖…...
助力网站优化利用AI批量生成文章工具提升质量
哎,有时候觉得写东西这事儿吧,真挺玄乎的。你看着那些大网站的优质内容,会不会突然冒出个念头——这些家伙到底怎么做到日更十篇还不秃头的?前阵子我蹲在咖啡馆里盯着屏幕发呆,突然刷到个帖子说现在用AI写文章能自动纠…...
Java语言的进化:JDK的未来版本
作为一名Java开发者,我们正处在一个令人兴奋的时代!Java语言正在以前所未有的速度进化,每个新版本都带来令人惊喜的特性。让我们一起探索JDK未来版本的发展方向,看看Java将如何继续领跑编程语言界!💪 &…...
SpringBootTest报错
Unable to find a SpringBootConfiguration, you need to use ContextConfiguration or … 解决方案:在SpringTest注解中添加属性(classes )填写启动类 如我的启动类是MainApplication.class javax.websocket.server.ServerContainer no…...
Flask + ajax上传文件(二)--多文件上传
Flask多文件上传完整教程 本教程将详细介绍如何使用Flask实现多文件上传功能,并使用时间戳为上传文件自动命名,避免文件名冲突。 一、环境准备 确保已安装Python和Flask pip install flask项目结构 flask_upload/ ├── app.py ├── upload/ # 上传文…...
w~视觉~合集3
我自己的原文哦~ https://blog.51cto.com/whaosoft/12327888 #几个论文 Fast Charging of Energy-dense Lithium-ion Batteries Real-time Short Video Recommendation on Mobile Devices Semantic interpretation for convolutional neural networks: What makes a ca…...
Redis安装及入门应用
应用资料:https://download.csdn.net/download/ly1h1/90685065 1.获取文件,并在该文件下执行cmd 2.输入redis-server-lucifer.exe redis.windows.conf,即可运行redis 3.安装redis客户端软件 4.安装后运行客户端软件,输入链接地址…...
NODE_OPTIONS=--openssl-legacy-provider vue-cli-service serve
//"dev": " NODE_OPTIONS--openssl-legacy-provider vue-cli-service serve" // 修改后(Windows 适用) "dev": "vue-cli-service serve --openssl-legacy-provider" 升级 Node.js 到 v14,确保依赖…...
如何在 Postman 中,自动获取 Token 并将其赋值到环境变量
在 Postman 中,你可以通过 预请求脚本(Pre-request Script) 和 测试脚本(Tests) 实现自动获取 Token 并将其赋值到环境变量,下面是完整的操作步骤: ✅ 一、创建获取 Token 的请求 通常这个请求…...
上篇:深入剖析 BLE 底层物理层与链路层(约5000字)
引言 在无线通信领域,Bluetooth Low Energy(BLE)以其超低功耗、灵活的连接模式和良好的生态支持,成为 IoT 与可穿戴设备的首选技术。要想在实际项目中优化性能、控制功耗、保证可靠通信,必须对 BLE 协议栈的底层细节有深入了解。本篇将重点围绕物理层(PHY)与链路层(Li…...
PostgreSQL 的 MVCC 机制了解
PostgreSQL 的 MVCC 机制了解 PostgreSQL 使用多版本并发控制(MVCC)作为其核心并发控制机制,这是它与许多其他数据库系统的关键区别之一。MVCC 允许读操作不阻塞写操作,写操作也不阻塞读操作,从而提供高度并发性。 一 MVCC 基本原理 1.1 M…...
【Pandas】pandas DataFrame dot
Pandas2.2 DataFrame Binary operator functions 方法描述DataFrame.add(other)用于执行 DataFrame 与另一个对象(如 DataFrame、Series 或标量)的逐元素加法操作DataFrame.add(other[, axis, level, fill_value])用于执行 DataFrame 与另一个对象&…...
2025 年“泰迪杯”数据挖掘挑战赛B题——基于穿戴装备的身体活动监测问题分析
摘要 本文聚焦于基于穿戴设备采集的加速度计数据,深入研究志愿者在日常活动中的行为特征,构建了多个数学建模框架,实现从身体活动监测、能耗预测、睡眠阶段识别到久坐预警等多个目标。我们依托于多源数据融合与机器学习模型,对人体活动状态进行识别与分析,为健康管理、行…...
Vivado版本升级后AXI4-Stream Data FIFO端口变化
Vivado 2017.4版本中异步AXI4-Stream Data FIFO升级到Vivado 2018.3后,IP管脚会发生变化,2018.3版中没有m_axis_aresetn和axis_data_count。 async_axis_fifo_8_1024 async_axis_fifo_8_1024 ( .s_axis_aresetn (I_do0_rstn ), // input wire…...
Linux424 chage密码信息 gpasswd 附属组
https://chat.deepseek.com/a/chat/s/e55a5e85-de97-450d-a19e-2c48f6669234...
Git 恢复误删除的文件
由于一些操作,把项目中的大量文件删除了,还以为之前敲得代码都付之东流了,突然想起,我的项目使用git进行的版本管理,且一些更改都暂存在本地的仓库的,因此可以使用git来恢复存入仓库的文件 首先࿰…...
自定义指令简介及用法(vue3)
一介绍 防抖与节流,应用场景有很多,例如:禁止重复提交数据的场景、搜索框输入搜索条件,待输入停止后再开始搜索。 防抖 点击button按钮,设置定时器,在规定的时间内再次点击会重置定时器重新计时…...
关于Qt对Html/CSS的支持
提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档 文章目录 前言一、原生控件二、QtWebEngine总结 前言 最近遇到了一些问题需要使用Qt加载Html发现一些特性不能使用,估计很多人也和我一样遇到这种情况。需要说明…...
海量数据笔试题--Top K 高频词汇统计
问题描述: 假设你有一个非常大的文本文件(例如,100GB),文件内容是按行存储的单词(或其他字符串,如 URL、搜索查询词等),单词之间可能由空格或换行符分隔。由于文件巨大&…...
Python函数与模块
简介 在Python编程中,函数和模块是实现代码复用、提高开发效率的核心机制。本文将结合理论与实例,解析Python函数与模块的核心知识点,帮助开发者打下基础。 一、函数 函数是一段可重复调用的代码块,通过参数传递实现灵活的逻辑…...
位运算题目:解码异或后的排列
文章目录 题目标题和出处难度题目描述要求示例数据范围 解法思路和算法代码复杂度分析 题目 标题和出处 标题:解码异或后的排列 出处:1734. 解码异或后的排列 难度 6 级 题目描述 要求 有一个整数数组 perm \texttt{perm} perm,是前…...
【Spring Boot】深入解析:#{} 和 ${}
1.#{} 和 ${}的使用 1.1数据准备 1.1.1.MySQL数据准备 (1)创建数据库: CREATE DATABASE mybatis_study DEFAULT CHARACTER SET utf8mb4;(2)使用数据库 -- 使⽤数据数据 USE mybatis_study;(3ÿ…...
linux:启动后,ubuntu屏幕变成红色了
屏幕启动后变成 红色背景 通常说明 显卡驱动出了问题,或者是 图形界面加载失败 使用了 fallback 模式。这种现象在 NVIDIA 驱动安装失败或显卡与驱动不兼容时常见。 🎯 先给你几个快速修复选项 ✅ 1. 进入 TTY 命令行界面 按下:Ctrl Alt …...
从实验室到产业端:解码 GPU 服务器的八大核心应用场景
一、深度学习与人工智能的基石 在深度学习领域,GPU 服务器的并行计算架构成为训练大规模模型的核心引擎 —— 传统 CPU 集群训练千亿参数模型需数月,而基于某国际知名芯片厂商 H100 的 GPU 服务器可将周期缩短至数周,国内科技巨头 910B 芯…...
java—12 kafka
目录 一、消息队列的优缺点 二、常用MQ 1. Kafka 2. RocketMQ 3. RabbitMQ 4. ActiveMQ 5. ZeroMQ 6. MQ选型对比 适用场景——从公司基础建设力量角度出发 适用场景——从业务场景角度出发 四、基本概念和操作 1. kafka常用术语 2. kafka常用指令 3. 单播消息&a…...
YOLOv8 涨点新方案:SlideLoss FocalLoss 优化,小目标检测效果炸裂!
YOLOv8优化秘籍:用SlideLoss和FocalLoss提升小目标检测精度(附代码实战) 📌 核心问题:YOLOv8在检测小物体时效果不够好? YOLOv8虽然是强大的目标检测模型,但在处理小物体或类别不平…...
数据库-数据类型、约束 和 DQL语言
标题目录 数据类型数字类型INT 型BIGINT 型DOUBLE 类型 字符类型定长字符串变长字符串 日期类型 约束主键约束非空约束唯一性约束检查约束外键约束 DQL 语言WHERE 子句连接多个条件IN (列表)NOT IN (列表)BETWEEN...AND...DISTINCT多字段去重 模糊查询NULL 值判断排序ÿ…...
verilog和system verilog常用数据类型以及常量汇总
int和unsigned 在 Verilog-2001 中,没有 int 和 unsigned 这样的数据类型。这些关键字是 SystemVerilog 的特性,而不是 Verilog-2001 的一部分。 Verilog-2001 的数据类型 在 Verilog-2001 中,支持的数据类型主要包括以下几种: …...
