Cookie、Session 与 Token:核心区别与应用场景解析
目录
引言
基础概念解析:三大身份验证技术详解
Cookie:浏览器中的"身份证"
Session:服务器记忆的"对话"
Token:加密的"通行证"
三种技术的深度对比分析
存储位置与数据流向
安全性全面对比
性能与扩展性考量
Token的现代应用场景与实践经验
跨域身份验证
分布式系统与微服务架构
移动应用与第三方API集成
用户体验优化:刷新Token机制
无Cookie环境下的Session实现方案
URL重写技术
隐藏表单字段
自定义请求头
无Cookie方案的局限性
总结
引言
作为一名开发者,你一定遇到过这样的问题:如何在用户浏览网站的不同页面时保持其登录状态?这个看似简单的需求,实际上涉及到了Web开发中的一个基础难题——HTTP协议的无状态性。
HTTP协议设计之初就是无状态的,这意味着服务器无法自动识别连续请求是否来自同一用户。每次请求都是独立的,就像萍水相逢的陌生人一样,服务器对请求的发送者一无所知。这种特性在提供简单信息时非常高效,但在需要用户登录、购物车等有状态场景中却带来了巨大挑战。
为了解决这一问题,Web技术发展出了三种主流方案:Cookie、Session和Token。接下来,我们将深入探讨这三种技术的工作原理、优缺点以及各自的最佳应用场景。
基础概念解析:三大身份验证技术详解
Cookie:浏览器中的"身份证"
定义与原理
Cookie本质上是一个存储在用户浏览器中的小型文本文件,由服务器生成并发送给浏览器。浏览器将其保存在本地,并在后续向同一域名发送请求时自动附加在HTTP请求头中。
Set-Cookie: username=muller; expires=Thu, 31 Dec 2024 23:59:59 GMT; path=/; domain=example.com; secure; httpOnly
上面的响应头示例展示了服务器如何设置Cookie,包含了值、过期时间、路径、域名等信息。
核心特性
- 域名绑定:Cookie严格遵循同源策略,只能被绑定的域名访问
- 容量限制:单个Cookie大小不超过4KB,且每个域名能设置的Cookie数量有限
- 生命周期:可设置过期时间,支持会话期Cookie(关闭浏览器即失效)或持久性Cookie
// 服务端设置Cookie(Node.js示例)
res.setHeader('Set-Cookie', 'userId=12345; Max-Age=86400; HttpOnly');// 前端JavaScript读取Cookie
console.log(document.cookie); // 输出所有非HttpOnly的Cookie
Session:服务器记忆的"对话"
定义与原理
Session是服务器端维护的一种用户会话状态机制。当用户首次访问网站时,服务器创建一个唯一的Session ID,通常通过Cookie发送给客户端保存。用户后续访问时,服务器通过这个ID找到对应的会话数据。

核心特性
- 服务器存储:会话数据存储在服务器端,客户端只保存Session ID
- 数据多样性:可以存储任意类型的数据,不受客户端限制
- 安全性:敏感信息不直接暴露给客户端
- 生命周期:一般与浏览器会话绑定,也可设置固定过期时间
实现示例
// Java Servlet中的Session使用示例
HttpSession session = request.getSession();
session.setAttribute("user", userObject);
session.setMaxInactiveInterval(1800); // 设置30分钟超时// 后续获取数据
User user = (User) session.getAttribute("user");
Token:加密的"通行证"
定义与原理
Token是一种现代化的身份验证方式,通常是服务器在用户认证成功后生成的加密字符串。最常见的实现是JWT(JSON Web Token),它包含头部、负载和签名三部分。
核心特性
- 无状态:服务器不需要存储Token信息,通过验证签名确认有效性
- 信息自包含:Token本身可以包含用户标识、权限等信息
- 跨域支持:不受同源策略限制,适合分布式系统
- 可编程过期时间:支持灵活设置有效期
JWT示例
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c
上面的JWT由三部分组成,用点号分隔,分别是:头部(算法和类型)、负载(包含声明的数据)和签名(用于验证)。
直通车:分布式应用下登录检验解决方案_登录检验token-CSDN博客
三种技术的深度对比分析
存储位置与数据流向
从技术架构角度看,三者存在根本差异:
- Cookie:完全存储在客户端,每次请求自动发送给服务器
- Session:核心数据存储在服务器,客户端只存Session ID(通常通过Cookie)
- Token:由服务器生成,存储和使用都在客户端,服务器无需保存状态
这种差异直接影响了系统的扩展性和性能。当用户量增大时,Session会占用大量服务器资源,而Token则将计算压力转移到了验证环节。
安全性全面对比
Cookie安全挑战:
- 易受XSS攻击(跨站脚本攻击):若未设置HttpOnly,JavaScript可读取Cookie
- 容易遭受CSRF攻击(跨站请求伪造):浏览器会自动附加Cookie
- 明文传输风险:若未使用HTTPS,Cookie可被网络嗅探获取
Session安全优势:
- 敏感数据存储在服务器,不直接暴露
- Session ID通常设置HttpOnly,防止JavaScript访问
- 可实现会话固定防护和会话劫持检测
Token安全特性:
- 签名机制确保数据完整性,防止篡改
- 无需存储在服务器,减少了会话劫持风险
- 可包含权限范围(scope),实现细粒度授权
- 支持设备绑定等高级安全机制
// Token安全增强示例:添加设备指纹
const payload = {userId: user.id,deviceFingerprint: generateFingerprint(req),exp: Math.floor(Date.now() / 1000) + 3600
};
const token = jwt.sign(payload, secretKey);
性能与扩展性考量
在大规模系统中,三种方案的性能表现各不相同:
Cookie:
- 优势:轻量级,对服务器几乎无负担
- 劣势:每次请求都会传输所有Cookie,增加网络开销
Session:
- 优势:灵活的数据结构,便于复杂状态管理
- 劣势:服务器内存消耗大,集群环境需额外同步机制
Token:
- 优势:无状态设计,易于水平扩展,适合微服务架构
- 劣势:Token体积可能较大,增加请求负载;解析与验证需CPU计算
在我的实践经验中,对于用户量在10万级别以下的系统,三者性能差异不明显;但当系统扩展到百万级用户时,Token的优势开始显现,特别是在微服务架构中。
Token的现代应用场景与实践经验
随着Web应用架构的演进,Token特别是JWT已成为主流认证方式。以下是几个典型应用场景:
跨域身份验证
现代Web应用经常需要跨域调用API。由于Cookie受同源策略限制,Token成为理想选择:
// 伪代码:React前端发送跨域请求示例
const fetchData = async () => {const response = await fetch('https://api.example.com/data', {headers: {'Authorization': `Bearer ${localStorage.getItem('token')}`}});const data = await response.json();setData(data);
};
分布式系统与微服务架构
在分布式环境中,Token无需集中式会话存储,每个服务只需验证Token有效性:
// 伪代码:Spring Boot中验证JWT
@GetMapping("/api/resources")
public ResponseEntity<?> getResources(HttpServletRequest request) {String token = extractTokenFromRequest(request);if (!jwtValidator.isValid(token)) {return ResponseEntity.status(HttpStatus.UNAUTHORIZED).build();}Claims claims = jwtParser.parseToken(token);String userId = claims.getSubject();// 处理业务逻辑return ResponseEntity.ok(resourceService.getResourcesForUser(userId));
}
移动应用与第三方API集成
移动应用通常需要与多个后端服务交互,Token提供了统一的认证方式:
// 伪代码:Swift中使用Token访问API
let url = URL(string: "https://api.example.com/data")!
var request = URLRequest(url: url)
request.httpMethod = "GET"
request.setValue("Bearer \(tokenString)", forHTTPHeaderField: "Authorization")URLSession.shared.dataTask(with: request) { data, response, error in// 处理响应
}.resume()
用户体验优化:刷新Token机制
一个常见挑战是Token过期后用户体验。通过实现刷新Token机制,可以在后台无缝续期:
// 前端拦截器实现自动刷新Token
axios.interceptors.response.use(response => response,async error => {const originalRequest = error.config;if (error.response.status === 401 && !originalRequest._retry) {originalRequest._retry = true;try {const refreshToken = localStorage.getItem('refreshToken');const response = await axios.post('/api/refresh-token', { refreshToken });localStorage.setItem('token', response.data.token);// 使用新token重试原请求originalRequest.headers['Authorization'] = `Bearer ${response.data.token}`;return axios(originalRequest);} catch (refreshError) {// 刷新失败,重定向到登录页window.location.href = '/login';return Promise.reject(refreshError);}}return Promise.reject(error);}
);
无Cookie环境下的Session实现方案
尽管Cookie是实现Session的主要手段,但在某些场景下(如禁用Cookie的浏览器或Cookie数量受限的网站),我们需要替代方案:
URL重写技术
URL重写是最常见的替代方法,通过在URL中附加Session ID参数:
https://example.com/profile?sessionId=a12b3c4d5e6f7g8h9i0j
服务端实现示例(Java):
// 检查Cookie是否可用,否则使用URL重写
if (request.getCookies() == null || !containsSessionCookie(request.getCookies())) {String encodedURL = response.encodeURL(baseURL);// encodedURL会自动添加sessionId参数return encodedURL;
}
隐藏表单字段
对于POST请求,可将Session ID嵌入表单:
<form action="/submit" method="POST"><input type="hidden" name="sessionId" value="a12b3c4d5e6f7g8h9i0j"><!-- 其他表单字段 --><button type="submit">提交</button>
</form>
<form action="/submit" method="POST"><input type="hidden" name="sessionId" value="a12b3c4d5e6f7g8h9i0j"><!-- 其他表单字段 --><button type="submit">提交</button>
</form>
自定义请求头
在AJAX请求中,可使用自定义头传递Session ID:
fetch('/api/data', {headers: {'X-Session-ID': 'a12b3c4d5e6f7g8h9i0j'}
});
无Cookie方案的局限性
这些替代方案虽然可行,但都存在明显缺点:
- 安全风险:Session ID暴露在URL中容易被截获
- 用户体验下降:URL变长、复杂,不便分享
- 实现复杂性:需处理所有链接和表单,工作量大
- 无法应对第三方资源:外部图片、CSS等请求无法附加Session ID
我建议尽可能避免这些方案,除非在特定场景中确实无法使用Cookie。
总结
通过本文的深入分析,我们可以看到:
- Cookie作为最古老的技术,依然在Web开发中扮演重要角色,特别是与Session配合使用
- Session提供了服务端可控的会话管理机制,适合安全性要求高的场景
- Token特别是JWT,已成为现代分布式系统的首选认证方式,其无状态特性带来了卓越的扩展性
相关文章:
Cookie、Session 与 Token:核心区别与应用场景解析
目录 引言 基础概念解析:三大身份验证技术详解 Cookie:浏览器中的"身份证" Session:服务器记忆的"对话" Token:加密的"通行证" 三种技术的深度对比分析 存储位置与数据流向 安全性全面对比…...
结合DrRacket学习《如何设计程序,第二版》
如何设计程序语言 DrRacket用于学习程序语言和设计程序语言,如何设计程序手册:How to Design Programs 如何设计程序,第二版:How to Design Programs, Second Edition 系统程序设计 设计师需遵循一系列严谨且系统的步骤&#…...
状态空间模型解析 (State-Space Model, SS)
一、文章摘要 状态空间模型(State-Space Model, SS)是一种广泛应用于控制系统、信号处理和系统建模的数学表示方式。MATLAB 提供的 ss 类用于描述线性时不变(LTI)系统的状态空间表示。本类实现了 LTI 系统的构造、属性设置、变换…...
zabbix添加IIS网站计数器(并发连接数)
0. Zabbix Agent 配置 PerfCounter = iis_Current_Connections,"\Web Service(_Total)\Current Connections",30 1.在被监控主机,powershell输入perfmon.msc 2.点击添加按钮 3.在下拉菜单中点击小箭头...
uniapp处理流式请求
在uniapp里面处理流式请求相对于web端来说有点麻烦,下面我将讲述几种处理流式请求的方式。 1.websocket WebSocket 是处理实时数据流的最佳选择之一,UniApp 提供了原生的 WebSocket 支持: <template><view class"container&…...
代码随想录算法训练营第四十一天|买卖股票专题:121. 买卖股票的最佳时机、122.买卖股票的最佳时机II、123.买卖股票的最佳时机III
动规五部曲牢记于心 1、确定好dp[j]数组,以及下标含义 2、推导出dp[j]公式 3、初始化,关键dp[0][0]、dp[0][1],第i天,后面的01表示状态:持有、不持有 4、确定遍历顺序: 如果求组合问题,不考虑排…...
AI小白的第七天:必要的数学知识(概率)
概率 Probability 1. 概率的定义 概率是一个介于 0 和 1 之间的数,表示某个事件发生的可能性: 0:事件不可能发生。1:事件必然发生。0 到 1 之间:事件发生的可能性大小。 例如,掷一枚公平的硬币…...
[Windows] 图吧工具箱
[Windows] 图吧工具箱 链接:https://pan.xunlei.com/s/VOMCXYDix3pvwdkU7w7bfVsDA1?pwdk8v5# DIY爱好者的必备工具...
Docker镜像迁移方案
Docker镜像迁移方案 文章目录 Docker镜像迁移方案一:背景二:操作方式三:异常原因参考: 一:背景 比如机器上已经有先有的容器,但是docker pull的时候是失败的二:操作方式 1、停止正在运行的容器…...
1264. 动态求连续区间和-acwing -树状数组
原题链接:1264. 动态求连续区间和 - AcWing题库 给定 n 个数组成的一个数列,规定有两种操作,一是修改某个元素,二是求子数列 [a,b] 的连续和。 输入格式 第一行包含两个整数 n 和m,分别表示数的个数和操作次数。 第…...
三分钟读懂微服务
一、什么是微服务 微服务,简单来说,就是把一个庞大复杂的软件系统,拆分成一个个小型的、独立的服务模块。打个比方,一个大型商场就如同传统的单体架构软件系统,里面所有的店铺、设施都紧密关联在一起。而微服务架构下…...
【AIGC】图片变视频 - SD ComfyUI视频生成
效果图 完整过程 SD ComfyUI 下载 下载 https://pan.quark.cn/s/64b808baa960 解压密码:bilibili-秋葉aaaki 完整 https://www.bilibili.com/video/BV1Ew411776J/ SD ComfyUI 安装 1.解压 2.将controlnet内部文件复制到 ComfyUI-aki-v1.6\ComfyUI\models\control…...
JVM详解(包括JVM内存模型与GC垃圾回收)
📖前言: 学会使用Java对于一个程序员是远远不够的。Java语法的掌握只是一部分,另一部分就是需要掌握Java内部的工作原理,从编译到运行,到底是谁在帮我们完成工作的? 接下来着重对Java虚拟机,也就…...
cocos creator 笔记-路边花草
版本:3.8.5 实现目标:给3d道路生成路边景观花草 在场景下创建一个节点,我这里种植两种花草模型,兰花和菊花,所以分别在节点下另创建两个节点,为了静态合批。 1.将花草模型分别拖入场景中,制作…...
在shell脚本内部获取该脚本所在目录的绝对路径
目录 需求描述 方法一:使用 dirname 和 readlink 命令 方法二:使用 BASH_SOURCE 变量 方法三:仅使用纯 Bash 实现 需求描述 工作中经常有这样情况,需要在脚本内部获取该脚本自己所在目录的绝对路径。 假如有一个脚本/a/b/c/…...
Qt 线程类
线程类 这些类与线程应用程序相关。 Concurrent Filter and Filter-Reduce 并行地从序列中选择值并组合它们 Concurrent Map and Map-Reduce 并行地从序列中转换值并组合它们 Concurrent Run 在单独线程中运行任务的简单方法 Concurrent Task 在独立线程中运行任务的可…...
Langchain中的表格解析:RAG 和表格的爱恨情仇
实现 RAG(Retrieval-Augmented Generation)是一个挑战,尤其是在有效解析和理解非结构化文档中的表格时。这在处理扫描文档或图像格式的文档时尤为困难。这些挑战至少包括以下三个方面: 1.表格的“叛逆期”:不准确的解析可能会破坏表格结构: 表格在文档里就像个叛逆的青少…...
神奇的闹钟(算法题)
神奇的闹钟 题目 原题 小蓝发现了一个神奇的闹钟,从纪元时间(19701970 年 11 月 11 日 00:00:0000:00:00)开始,每经过 xx 分钟,这个闹钟便会触发一次闹铃 (纪元时间也会…...
CAT1模块 EC800M HTTP 使用后续记录
记录一下 CAT1 模块EC800 HTTP 使用后续遇到的问题 by 矜辰所致目录 前言一、一些功能的完善1.1 新的交互指令添加1.2 连不上网络处理 二、问题出现三、分析及解决3.1 定位问题3.2 问题分析与解决3.2.1 查看变量在内存中的位置 3.3 数据类型说明3.3.1 常用格式化输出符号…...
Python 标准库与数据结构
Python的标准库提供了丰富的内置数据结构和函数,使用这些工具能为我们提供一套强有力的工具。 需要注意的是,相比C与Java,Python的一些特点: Python不需要显式声明变量类型Python没有模板(Template)的概念,因为Pytho…...
NIO入门
IO和NIO的区别: IO:通过流处理数据,仅支持阻塞IO。 核心组件:InputStream /OutputStream用于字节的读写,Reader / Writer:用于字符流的读写。读取过程中无法被中断,是阻塞式IO。 NIO:通过管道处…...
leetcode 用队列模拟栈
这个其实只需要一个队列就可以的,但是我这里用的是2个队列进行替换, 先转n-1个到空的队列, 然后在此基础上进行队列的互换,把剩下的那一个元素所在的队列进行poleft操作就可以了。 class MyStack:def __init__(self):self.q1_i…...
spring security 使用的过滤器还是拦截器
spring security 使用的过滤器还是拦截器 Spring Security 是一个强大的安全框架,用于保护 Java 应用程序。它主要使用过滤器(Filters)来实现安全功能,而不是拦截器(Interceptors)。不过,它也提…...
大疆上云api介绍
概述 目前对于 DJI 无人机接入第三方云平台,主要是基于 MSDK 开发定制 App,然后自己定义私有上云通信协议连接到云平台中。这样对于核心业务是开发云平台,无人机只是其中一个接入硬件设备的开发者来说,重新基于 MSDK 开发 App 工作量大、成本高,同时还需要花很多精力在无人…...
2025-03-25 Unity 网络基础4——TCP同步通信
文章目录 1 Socket1.1 Socket 类型1.2 构造 Socket1.3 常用属性1.4 常用方法 2 TCP 通信2.1 服务端配置2.2 客户端配置2.3 进行通信2.4 多设备通信 3 区分消息 1 Socket Socket 是 C# 提供的网络通信类(其它语言也有对应的 Socket 类),是…...
C++进阶(一)
个人主页:PingdiGuo_guo 收录专栏:C干货专栏 前言 本篇博客是讲解函数的重载以及引用的知识点的。 文章目录 前言 1.函数重载 1.1何为函数重载 1.2函数重载的作用 1.3函数重载的实现 2.引用 2.1何为引用 2.2定义引用 2.3引用特性 2.4常引用 2…...
深度解读DeepSeek:开源周(Open Source Week)技术解读
深度解读DeepSeek:开源周(Open Source Week)技术解读 深度解读DeepSeek:源码解读 DeepSeek-V3 深度解读DeepSeek:技术原理 深度解读DeepSeek:发展历程 文章目录 一、开源内容概览Day1:FlashMLAD…...
AI Agent开发与应用
AI Agent开发与应用:本地化智能体实践——本地化智能体开发进展与主流框架分析 我要说的都在ppt里面了,相关复现工作请参考ai agent开发实例 OpenManus Dify Owl 第二个版本更新了对话的框架,通过gradio做了一个全新的界面 只测试了阿里云…...
石斛基因组-文献精读122
A chromosome-level Dendrobium moniliforme genome assembly reveals the regulatory mechanisms of flavonoid and carotenoid biosynthesis pathways 《染色体水平的石斛基因组组装揭示了黄酮类和胡萝卜素生物合成途径的调控机制》 摘要 石斛(Dendrobium monil…...
javaSE.多维数组
1 final 引用类型 final int[] arr 继承Object 的引用类型,不能改变引用的对象 存的其实是引用 数组类型数组,其实存的是引用 int [][] arr new int[][] { {1,2,3}, {4,5,6} };int [] a arr[0]; int [] b arr[1];...
