第一部分:核心概念详解
一、基础概念
1. 认证(Authentication)
- 定义:验证"你是谁",确认身份
- 例子:登录时验证用户名密码
- 类比:出示身份证证明身份
2. 授权(Authorization)
- 定义:验证"你能做什么",确认权限
- 例子:登录后检查是否有权限访问某个功能
- 类比:有会员卡,但不同等级权限不同
3. Cookie(小饼干)
- 定义:服务器发给浏览器的一小段文本,浏览器保存并在后续请求中自动发送
- 特点:
- 存储在浏览器(客户端)
- 每次请求自动发送
- 有大小限制(通常4KB)
- 可设置过期时间
- 可设置作用域(域名/路径)
- 格式示例: Set-Cookie: auth_session=abc123; Path=/; HttpOnly; Expires=Wed, 21 Oct 2025 07:28:00 GMT
- 组成部分:
- 名称和值:auth_session=abc123
- 路径:Path=/(哪些路径可用)
- 域名:Domain=.example.com(哪些域名可用)
- 过期时间:Expires=...(何时失效)
- 安全标志:
- HttpOnly:只能通过HTTP访问,JavaScript不能读取
- Secure:只能通过HTTPS传输
4. Session(会话)
- 定义:服务器端存储的用户会话信息,用SessionID标识
- 特点:
- 存储在服务器(内存或Redis/数据库)
- 每个用户有唯一SessionID
- SessionID通常通过Cookie传递
- 服务器可根据SessionID查找会话数据
-
存储内容示例: SessionID: sess_abc123
存储数据: {
userId: "123",
username: "zhangsan",
loginTime: "2024-01-01 10:00:00"
}
5. SessionID(会话标识符)
- 定义:Session的唯一标识符
- 特点:
- 通常是一串随机字符串
- 通过Cookie传递给客户端
- 服务器用SessionID查找对应的Session
- 示例:sess_abc123def456
6. Token(令牌)
- 定义:用于身份验证的凭证
- 特点:
- 可以是任意格式的字符串
- 包含身份信息或指向身份信息的标识
- 有有效期
- 常见类型:
- 随机字符串Token(如:abc123def456)
- JWT Token(结构化Token)
- API Key(简单密钥)
7. JWT(JSON Web Token)
- 定义:标准化的Token格式,包含用户信息,经过签名,可自验证
- 结构(三部分,用.分隔): Header.Payload.Signature
- 三部分详解:
-
Header(头部):说明类型和签名算法 {
"alg": "HS256", // 签名算法
"typ": "JWT" // Token类型
}
-
Payload(载荷):存放用户信息 {
"userId": "123",
"username": "zhangsan",
"exp": 1234567890, // 过期时间
"iat": 1234567800 // 签发时间
}
-
Signature(签名):用密钥对Header和Payload进行签名 HMACSHA256(
base64UrlEncode(header) + "." + base64UrlEncode(payload),
secret
)
8. 签名(Signature)
- 定义:用密钥对内容进行加密计算的结果,用于防篡改
- 作用:
- 证明Token由你的服务器签发
- 防止Token被篡改
- 工作原理:
- 生成时:用密钥对内容计算签名
- 验证时:用同样的密钥重新计算签名,对比是否一致
9. 密钥(Secret Key)
- 定义:用于生成和验证签名的关键字符串,必须保密
- 特点:
- 只有服务器知道
- 如果泄露,攻击者可以伪造Token
- 必须足够复杂和安全
10. Base64编码
- 定义:将二进制数据转换为可打印字符的编码方式
- 作用:JWT中用于编码Header和Payload
- 特点:
- 不是加密,只是编码
- 可以轻松解码
- 用于传输,不用于安全
11. HMAC-SHA256
- 定义:一种加密算法,用于生成签名
- 工作原理:
- 输入:内容和密钥
- 输出:固定长度的签名
- 特点:相同输入产生相同输出,不同输入产生不同输出
12. 过期时间(Expiration Time)
- 定义:Token或Session的有效期限
- 作用:
- 限制Token的使用时间
- 提高安全性(即使Token泄露,也会过期)
- 常见设置:
- 短期Token:1小时、24小时
- 长期Token:7天、30天
13. 黑名单(Blacklist)
- 定义:已失效但未过期的Token列表
- 作用:
- 实现Token注销功能
- 即使Token未过期,也可以使其失效
- 存储方式:通常存储在Redis中
14. 有状态(Stateful)
- 定义:服务器需要存储会话信息
- 特点:
- 服务器需要存储Session
- 需要查询数据库或缓存
- 扩展性较差(多服务器需要共享Session)
- 例子:Session认证
15. 无状态(Stateless)
- 定义:服务器不需要存储会话信息
- 特点:
- 服务器不需要存储
- 所有信息都在Token中
- 扩展性好(任何服务器都可以验证)
- 例子:JWT认证
二、认证方式概念
1. Session认证
- 定义:使用Session存储用户信息的认证方式
- 特点:有状态、服务器存储、可主动注销
- 流程:登录→创建Session→返回SessionID→后续请求验证Session
2. JWT认证
- 定义:使用JWT Token的认证方式
- 特点:无状态、客户端存储、无法主动注销
- 流程:登录→生成JWT→返回Token→后续请求验证Token
3. OAuth 2.0
- 定义:第三方授权标准
- 特点:标准化、支持第三方登录、流程复杂
- 流程:用户授权→获取授权码→换取Token→访问资源
4. API Key认证
- 定义:使用简单密钥的认证方式
- 特点:简单、性能好、安全性较低
- 流程:生成Key→请求携带Key→验证Key
第二部分:完整内容讲解
一、认证的本质和目的
1. 认证的目的
- 确认用户身份:验证"你是谁"
- 建立信任关系:登录后给凭证
- 保护资源:只有已登录用户才能访问
- 追踪用户:记录谁在做什么操作
2. 认证的两个阶段
- 阶段1:登录(建立信任)
- 用户提交凭证(用户名密码)
- 服务器验证凭证
- 服务器生成Token
- 返回Token给客户端
- 阶段2:验证(确认信任)
- 用户携带Token
- 服务器验证Token
- 确认是已登录用户
- 允许访问资源
二、Session认证详解
1. 工作原理
用户登录
↓
服务器验证用户名密码
↓
服务器创建Session,存储用户信息
↓
服务器生成SessionID
↓
服务器通过Cookie返回SessionID
↓
浏览器保存Cookie
↓
后续请求自动携带Cookie
↓
服务器根据SessionID查找Session
↓
从Session中获取用户信息
↓
允许访问
2. 详细流程
- 登录时:
- 用户提交用户名密码
- 服务器验证
- 创建Session,存储用户信息
- 生成SessionID(如:sess_abc123)
- 通过Set-Cookie返回SessionID
- 浏览器保存Cookie
- 后续请求时:
- 浏览器自动在请求头携带Cookie
- 服务器提取SessionID
- 根据SessionID查找Session
- 从Session中获取用户信息
- 验证通过,允许访问
3. 优点
- 可主动注销:删除Session即可
- 安全性好:敏感信息在服务器
- 可存储较多信息:Session可以存储任意数据
4. 缺点
- 需要服务器存储:增加服务器负担
- 扩展性差:多服务器需要共享Session
- 性能开销:每次请求需要查询Session
5. 适用场景
- 传统Web应用
- 单体应用
- 需要频繁注销的场景
三、JWT认证详解
1. JWT的结构
完整Token = Header.Payload.Signature
例如:
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VySWQiOiIxMjMiLCJ1c2VybmFtZSI6InpoYW5nc2FuIn0.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c
2. JWT的生成过程
- 步骤1:准备内容
- Header:{"alg":"HS256","typ":"JWT"}
- Payload:{"userId":"123","username":"zhangsan"}
- 步骤2:Base64编码
- 编码Header和Payload
- 步骤3:拼接
- 待签名内容 = Header + "." + Payload
- 步骤4:用密钥签名
- Signature = HMAC-SHA256(待签名内容, 密钥)
- 步骤5:Base64编码签名
- 编码Signature
- 步骤6:组合
- 完整Token = Header + "." + Payload + "." + Signature
3. JWT的验证过程
- 步骤1:拆分Token
- 提取Header、Payload、Signature
- 步骤2:重新计算签名
- 用同样的密钥重新计算签名
- 步骤3:对比签名
- 如果新签名 == Token中的Signature → 验证通过
- 如果新签名 != Token中的Signature → 验证失败
- 步骤4:检查过期时间
- 从Payload中提取exp字段
- 如果当前时间 > exp → Token已过期
- 步骤5:提取用户信息
- 从Payload中提取用户信息(userId、username等)
4. 为什么签名能保证安全
- 只有拥有密钥的服务器才能生成正确签名
- 如果内容被改,重新计算的签名会不同
- 如果签名被改,验证会失败
- 攻击者不知道密钥,无法伪造签名
5. 优点
- 无状态:服务器不需要存储
- 跨域友好:可以跨域使用
- 可携带用户信息:Payload中可以存储用户信息
- 适合微服务:任何服务器都可以验证
6. 缺点
- 无法主动注销:需要黑名单机制
- Token较大:比SessionID大很多
- 泄露风险:如果Token泄露,在过期前都可以使用
7. 适用场景
- 微服务架构
- 移动端应用
- 跨域场景
- 需要无状态认证的场景
四、Cookie和Session的关系
1. 关系说明
- Cookie是传递SessionID的载体
- Session是服务器端的会话存储
- SessionID是Session的唯一标识符
- 通常用Cookie传递SessionID
2. 工作流程
用户登录
↓
服务器创建Session(存储用户信息)
↓
服务器生成SessionID
↓
服务器通过Cookie返回SessionID
Set-Cookie: JSESSIONID=sess_abc123
↓
浏览器保存Cookie
↓
后续请求自动携带Cookie
Cookie: JSESSIONID=sess_abc123
↓
服务器根据SessionID查找Session
↓
从Session中获取用户信息
3. 类比
- Cookie = 会员卡(上面有会员号)
- Session = 会员档案(存在店里的系统里)
- SessionID = 会员号(写在会员卡上)
五、项目中的混合认证机制
1. 为什么需要混合机制
- 跨服务认证:wflow服务需要调用table服务
- table服务不直接信任wflow服务
- 需要一种跨服务的认证方式
2. 完整流程
- 第一步:生成JWT Token
- wflow服务获取用户信息
- 将用户信息放入JWT的Payload
- 用密钥签名生成Token
- 第二步:用JWT Token换取Session Cookie
- wflow构造URL:/api/auth/user-token/{jwtToken}
- 向table服务发送GET请求
- table服务验证Token签名
- table服务解析Payload获取用户信息
- table服务创建Session,存储用户信息
- table服务生成SessionID
- table服务通过Set-Cookie返回auth_session=xxx
- wflow从响应头提取Cookie
- 第三步:使用Session Cookie进行后续请求
- wflow在后续请求的Cookie头中带上auth_session=xxx
- table服务根据SessionID查找Session
- table服务从Session中获取用户信息
- 验证通过,处理业务请求
3. 为什么这样设计
- 跨服务信任:JWT作为临时凭证,table服务可以验证用户身份
- 性能优化:后续请求用Session,避免重复解析JWT
- 可控性:table服务可以管理Session的生命周期
六、如何验证用户身份
1. JWT能验证什么
- Token是否由你的服务器签发(签名验证)
- Token是否被篡改(内容完整性)
- Token是否过期(时间有效性)
2. JWT不能验证什么
- 用户是否真的是你的用户(身份真实性)
- 用户是否还有权限(权限有效性)
- 用户是否被禁用(账户状态)
3. 完整的用户验证流程
- 步骤1:验证Token本身(JWT验证)
- 验证Token格式
- 验证签名
- 验证是否过期
- 从Payload中提取userId
- 步骤2:验证用户身份(数据库验证)
- 用提取的userId查询数据库
- 检查用户是否存在
- 检查用户状态(是否被禁用、是否被删除)
- 检查用户权限
- 步骤3:验证会话状态(可选)
- 检查Token是否在黑名单中
- 检查用户的登录状态
- 检查用户最近是否修改了密码
七、认证方式对比
| 认证方式 | 存储位置 | 状态 | 主要优点 | 主要缺点 | 典型场景 |
|---|---|---|---|---|---|
| Session | 服务器 | 有状态 | 可主动注销、安全性好 | 需要存储、扩展性差 | 传统Web应用 |
| JWT | 客户端 | 无状态 | 无状态、跨域友好 | 无法主动注销、Token较大 | 微服务、移动端 |
| OAuth 2.0 | 授权服务器 | 混合 | 标准化、支持第三方 | 复杂度高 | 第三方登录 |
| API Key | 客户端 | 无状态 | 简单、性能好 | 安全性较低 | 服务间调用 |
八、关键要点总结
1. 认证的本质
- 登录 = 建立信任关系
- 验证 = 确认信任关系
- Token = 身份凭证
2. 核心概念
- Cookie:浏览器存储的小文本
- Session:服务器端的会话存储
- SessionID:Session的唯一标识符
- Token:身份验证的凭证
- JWT:标准化的Token格式
- 签名:防篡改的机制
3. 验证流程
- 每次请求都要验证Token
- 确保请求来自已登录用户
- 防止未授权访问
- 可以检查用户状态
4. 安全要点
- 密钥必须保密
- Token有有效期
- 使用HTTPS传输
- 定期更新Token
为什么要把 Token 和 Session 做比较?
一、核心原因
1. 它们解决同一个问题,但方式不同
- 问题:如何验证用户身份
- Session:服务器存储用户信息,用 SessionID 标识
- Token:客户端携带用户信息,用签名保证安全
- 结论:需要比较以选择合适方案
2. 它们有本质区别
- Session:有状态(服务器存储)
- Token:无状态(客户端存储)
- 结论:理解区别有助于正确使用
3. 项目使用了混合机制
- 项目同时使用了 JWT Token 和 Session Cookie
- 需要理解两者如何配合
- 结论:比较有助于理解混合机制
二、Token 和 Session 的本质区别
1. 存储位置不同
Session:
用户信息存储在哪里?
→ 服务器端(内存/Redis/数据库)
客户端存储什么?
→ 只存储SessionID(一个标识符)
类比:
→ 银行存钱,你只拿存折(SessionID),钱在银行(Session)
Token(JWT):
用户信息存储在哪里?
→ 客户端(Token的Payload中)
服务器存储什么?
→ 不存储,只存储密钥用于验证
类比:
→ 现金(Token),钱在你手里,不需要银行
2. 验证方式不同
Session:
验证流程:
-
客户端发送SessionID
-
服务器根据SessionID查找Session
-
从Session中获取用户信息
-
验证通过
需要查询数据库/缓存:
→ 每次请求都要查询Session
Token(JWT):
验证流程:
-
客户端发送Token
-
服务器验证Token签名
-
从Token的Payload中提取用户信息
-
验证通过
不需要查询数据库:
→ 只需要验证签名,不需要查询
3. 状态管理不同
Session(有状态):
服务器需要:
→ 存储每个用户的Session
→ 管理Session的生命周期
→ 处理Session的过期和清理
多服务器问题:
→ 需要共享Session(Redis/数据库)
→ 否则用户在不同服务器登录状态不一致
Token(无状态):
服务器不需要:
→ 不存储任何会话信息
→ 所有信息都在Token中
多服务器优势:
→ 任何服务器都可以验证Token
→ 不需要共享存储
→ 天然支持水平扩展
三、为什么需要比较它们?
1. 选择合适方案
场景1:传统Web应用(单体)
推荐:Session认证
原因:
-
用户在同一服务器上
-
需要频繁注销功能
-
安全性要求高
场景2:微服务架构
推荐:JWT Token认证
原因:
-
多个服务需要验证用户
-
不需要共享Session存储
-
性能要求高
场景3:移动端应用
推荐:JWT Token认证
原因:
-
无状态,适合移动端
-
不需要维护Session
-
跨域友好
2. 理解混合使用
项目中的混合机制:
为什么用JWT Token?
→ 跨服务认证(wflow服务调用table服务)
为什么用Session Cookie?
→ 性能优化(避免重复解析JWT)
如何配合?
→ JWT Token换取Session Cookie
→ 后续请求用Session Cookie
3. 避免常见误区
误区1:认为Token就是Session
错误理解:
→ Token和Session是一样的
正确理解:
→ Token是凭证,Session是存储
→ 它们可以配合使用,但不是同一个东西
误区2:认为有Token就不需要Session
错误理解:
→ 用了Token就不需要Session了
正确理解:
→ 可以只用Token(纯JWT)
→ 也可以Token+Session(混合机制)
→ 根据场景选择
误区3:认为Session比Token安全
错误理解:
→ Session更安全,Token不安全
正确理解:
→ 都有安全风险
→ Session:需要保护SessionID不被盗用
→ Token:需要保护密钥不被泄露
→ 安全性取决于实现方式,不是机制本身
四、Token 和 Session 的关系
1. 它们不是对立的
不是:
→ Token vs Session(二选一)
而是:
→ 两种不同的认证机制
→ 可以单独使用
→ 也可以配合使用
2. 它们可以配合使用
项目中的实际应用:
第一步:生成JWT Token
→ wflow服务生成Token(包含用户信息)
第二步:用Token换取Session
→ table服务验证Token
→ table服务创建Session
→ table服务返回Session Cookie
第三步:使用Session
→ 后续请求用Session Cookie
→ 避免重复解析JWT
3. 它们各有优势
Session的优势:
✅ 可以主动注销
✅ 服务器完全控制
✅ 可以存储大量信息
✅ 安全性好(敏感信息在服务器)
Token的优势:
✅ 无状态,扩展性好
✅ 跨域友好
✅ 性能好(不需要查询)
✅ 适合微服务
五、为什么项目要混合使用?
1. 跨服务认证需求
问题:
→ wflow服务需要调用table服务
→ table服务不直接信任wflow服务
解决:
→ 用JWT Token作为跨服务的凭证
→ table服务可以验证Token,确认用户身份
2. 性能优化
问题:
→ 如果每次请求都解析JWT,性能开销大
解决:
→ 第一次用JWT Token换取Session
→ 后续请求用Session Cookie
→ 避免重复解析JWT
3. 可控性
问题:
→ JWT Token无法主动注销
解决:
→ 用Session管理会话
→ 可以主动注销Session
→ 可以管理Session的生命周期
六、总结:为什么需要比较
1. 理解本质区别
- Session = 服务器存储 + SessionID标识
- Token = 客户端存储 + 签名验证
- 理解区别才能正确使用
2. 选择合适的方案
- 不同场景适合不同方案
- 比较有助于选择
- 也可以混合使用
3. 理解项目设计
- 项目使用了混合机制
- 需要理解两者如何配合
- 理解设计意图
4. 避免常见误区
- 它们不是对立的
- 它们可以配合使用
- 各有优势和适用场景
七、通俗总结
类比说明:
Session认证:
就像去银行存钱:
-
你把钱存在银行(Session存储在服务器)
-
你拿存折(SessionID)
-
每次取钱都要去银行(查询Session)
-
银行可以冻结你的账户(主动注销)
Token认证:
就像用现金:
-
钱在你手里(Token在客户端)
-
不需要银行(不需要服务器存储)
-
直接使用(验证签名即可)
-
但丢了就没了(无法主动注销)
混合机制:
就像先用现金换会员卡:
-
第一次:用现金(JWT Token)换会员卡(Session Cookie)
-
后续:用会员卡(Session Cookie)消费
-
既解决了跨服务问题,又优化了性能
八、关键要点
- Token 和 Session 不是对立的
- 它们是不同的认证机制
- 可以单独使用,也可以配合使用
- 比较的目的是理解区别
- 存储位置不同
- 验证方式不同
- 适用场景不同
- 项目混合使用的原因
- 跨服务认证(JWT Token)
- 性能优化(Session Cookie)
- 可控性(Session管理)
- 选择合适的方案
- 根据场景选择
- 可以单独使用
- 也可以混合使用
这就是为什么要把 Token 和 Session 做比较的原因。它们解决同一个问题,但方式不同,理解它们的区别和关系,有助于正确使用和选择合适的方案。
一、什么叫"有状态"?
1. 有状态(Stateful)的定义
有状态 = 服务器需要记住/存储信息
生活比喻:
有状态就像:
-
你去银行存钱,银行要记录你的账户信息
-
你去餐厅办会员卡,餐厅要记录你的会员信息
-
你去医院看病,医院要记录你的病历
关键:服务器需要"记住"每个用户的信息
2. Session 为什么是有状态的?
用户A登录
↓
服务器创建Session,存储:
{
sessionId: "sess_abc123",
用户信息: {
userId: "123",
username: "zhangsan",
loginTime: "2024-01-01 10:00:00"
}
}
↓
服务器需要"记住"这个Session
↓
存储在:内存/Redis/数据库
↓
这就是"有状态"
关键点:
- 服务器需要存储每个用户的Session
- 需要管理Session的生命周期
- 需要查询数据库/缓存来获取Session信息
3. 无状态(Stateless)的定义
无状态 = 服务器不需要记住/存储信息
生活比喻:
无状态就像:
-
你用现金买东西,商店不需要记住你是谁
-
你出示身份证,警察验证后就不需要记住你
-
你出示会员卡,店员验证后就不需要记住你
关键:服务器不需要"记住"任何信息
4. JWT Token 为什么是无状态的?
用户A登录
↓
服务器生成JWT Token:
{
Header: {...},
Payload: {
userId: "123",
username: "zhangsan"
},
Signature: "..."
}
↓
服务器返回Token给客户端
↓
服务器不需要存储任何信息
↓
后续请求:
客户端发送Token
↓
服务器验证Token签名(不需要查询数据库)
↓
从Token中提取用户信息
↓
这就是"无状态"
关键点:
- 服务器不需要存储任何会话信息
- 所有信息都在Token中
- 验证时只需要验证签名,不需要查询
5. 对比总结
| 特性 | 有状态(Session) | 无状态(JWT Token) |
|---|---|---|
| 服务器存储 | 需要存储Session | 不需要存储 |
| 查询数据库 | 每次请求都要查询 | 不需要查询 |
| 扩展性 | 多服务器需要共享Session | 任何服务器都可以验证 |
| 性能 | 需要查询,性能较差 | 不需要查询,性能好 |
| 可控性 | 可以主动注销 | 无法主动注销 |
二、SessionID 被乱猜怎么办?
1. 问题的本质
你的担心:
如果攻击者乱猜一个SessionID,比如:
sess_abc123
如果这个SessionID确实存在怎么办?
攻击者是不是就可以冒充这个用户了?
2. SessionID 的安全性设计
设计1:SessionID 是随机生成的
SessionID不是简单的数字:
❌ 不是:sess_1, sess_2, sess_3(容易被猜中)
✅ 而是:sess_a8f3b2c1d9e4f5g6h7i8j9k0(随机字符串)
特点:
- SessionID是随机生成的
- 长度足够长(通常32-128字符)
- 包含字母、数字、特殊字符
- 几乎不可能被猜中
设计2:SessionID 的生成方式
常见的生成方式:
- UUID(通用唯一标识符)
→ 例如:550e8400-e29b-41d4-a716-446655440000
→ 可能性:2^122 种组合(几乎不可能重复)
- 随机字符串
→ 例如:a8f3b2c1d9e4f5g6h7i8j9k0l1m2n3o4p5
→ 长度:32-128字符
→ 可能性:天文数字
- 加密算法生成
→ 使用加密算法生成随机字符串
→ 更加安全
设计3:SessionID 的验证机制
即使攻击者猜中了一个SessionID,还有多层保护:
保护1:Session 有过期时间
Session不是永久存在的:
-
通常设置过期时间(如:30分钟、1小时)
-
过期后Session自动失效
-
即使猜中,也可能已经过期
保护2:Session 可以绑定IP地址
服务器可以记录:
-
SessionID: sess_abc123
-
绑定的IP: 192.168.1.100
如果攻击者用不同的IP访问:
-
服务器检测到IP不匹配
-
拒绝访问,要求重新登录
保护3:Session 可以绑定User-Agent
服务器可以记录:
-
SessionID: sess_abc123
-
绑定的User-Agent: Mozilla/5.0...
如果攻击者用不同的浏览器访问:
-
服务器检测到User-Agent不匹配
-
拒绝访问,要求重新登录
保护4:Session 可以记录最后访问时间
服务器可以检测:
-
SessionID: sess_abc123
-
最后访问时间: 2024-01-01 10:00:00
-
当前时间: 2024-01-01 15:00:00
如果时间间隔异常:
-
服务器可以要求重新验证
-
或者直接使Session失效
3. 实际攻击场景分析
场景1:暴力猜测SessionID
攻击者尝试:
sess_1, sess_2, sess_3, ..., sess_1000000
问题:
-
SessionID是随机字符串,不是连续数字
-
即使尝试100万次,猜中的概率也几乎为0
-
服务器可以检测异常请求,封禁IP
场景2:SessionID 泄露
如果SessionID真的泄露了(比如被截获):
-
攻击者可以使用这个SessionID
-
但Session有过期时间
-
服务器可以检测异常行为,主动使Session失效
-
用户重新登录,旧Session失效
场景3:Session 固定攻击
攻击者诱导用户使用他提供的SessionID:
-
攻击者生成一个SessionID
-
诱导用户使用这个SessionID登录
-
攻击者就可以使用这个SessionID
防护:
-
每次登录都生成新的SessionID
-
不信任用户提供的SessionID
4. 最佳实践
实践1:使用HTTPS
HTTPS加密传输:
-
SessionID在传输过程中被加密
-
攻击者无法截获SessionID
实践2:设置HttpOnly标志
Set-Cookie: JSESSIONID=sess_abc123; HttpOnly
作用:
-
JavaScript无法读取Cookie
-
防止XSS攻击窃取SessionID
实践3:设置Secure标志
Set-Cookie: JSESSIONID=sess_abc123; Secure
作用:
-
只能通过HTTPS传输
-
防止在HTTP中被截获
实践4:定期更换SessionID
每次重要操作后:
-
生成新的SessionID
-
使旧SessionID失效
-
防止SessionID被长期使用
三、混合场景详解
1. 混合场景是什么?
混合场景 = JWT Token + Session Cookie 配合使用
不是只用JWT Token
也不是只用Session
而是:先用JWT Token,再换成Session Cookie
2. 为什么需要混合场景?
问题1:跨服务认证
场景:
-
wflow服务需要调用table服务
-
table服务不直接信任wflow服务
-
需要一种跨服务的认证方式
解决:
-
wflow服务生成JWT Token(证明用户身份)
-
table服务验证JWT Token(确认用户身份)
问题2:性能优化
如果只用JWT Token:
-
每次请求都要解析JWT
-
解析JWT需要计算签名
-
性能开销大
如果只用Session:
-
第一次请求需要创建Session
-
但无法跨服务使用
混合方案:
-
第一次:用JWT Token换取Session Cookie
-
后续:用Session Cookie(性能好)
3. 混合场景的完整流程
第一步:生成JWT Token
wflow服务:
-
获取用户信息(userId, username, email)
-
生成JWT Token
{
Header: {...},
Payload: {
userId: "123",
username: "zhangsan",
email: "zhangsan@example.com"
},
Signature: "..."
}
- Token包含用户身份信息
第二步:用JWT Token换取Session Cookie
wflow服务 → table服务:
-
构造URL:/api/auth/user-token/{jwtToken}
-
发送GET请求,URL中携带Token
table服务收到请求:
-
从URL中提取JWT Token
-
验证Token签名(确认Token有效)
-
从Token的Payload中提取用户信息
-
创建Session,存储用户信息
{
sessionId: "sess_abc123",
用户信息: {
userId: "123",
username: "zhangsan",
email: "zhangsan@example.com"
}
}
-
生成SessionID:sess_abc123
-
通过Set-Cookie返回Session Cookie
Set-Cookie: auth_session=sess_abc123
wflow服务:
-
从响应头中提取Cookie
-
得到:auth_session=sess_abc123
第三步:使用Session Cookie进行后续请求
wflow服务 → table服务:
- 在请求头中携带Cookie
Cookie: auth_session=sess_abc123
table服务收到请求:
-
从Cookie中提取SessionID
-
根据SessionID查找Session
-
从Session中获取用户信息
-
验证通过,处理业务请求
4. 混合场景的优势
优势1:解决跨服务认证
问题:
-
wflow服务和table服务是独立的
-
table服务不直接信任wflow服务
解决:
-
JWT Token作为跨服务的凭证
-
table服务可以验证Token,确认用户身份
优势2:性能优化
如果只用JWT Token:
-
每次请求都要解析JWT
-
解析需要计算签名,性能开销大
混合方案:
-
第一次:解析JWT(一次开销)
-
后续:使用Session(查询缓存,性能好)
优势3:可控性
如果只用JWT Token:
-
无法主动注销
-
Token在过期前一直有效
混合方案:
-
可以主动注销Session
-
可以管理Session的生命周期
5. 混合场景的流程图
┌─────────────────────────────────────────────────────────┐
│ 步骤1:生成JWT Token │
│ wflow服务生成Token(包含用户信息) │
└─────────────────────────────────────────────────────────┘
↓
┌─────────────────────────────────────────────────────────┐
│ 步骤2:用Token换取Session Cookie │
│ wflow → table: GET /api/auth/user-token/{token} │
│ table验证Token → 创建Session → 返回Cookie │
└─────────────────────────────────────────────────────────┘
↓
┌─────────────────────────────────────────────────────────┐
│ 步骤3:使用Session Cookie │
│ wflow → table: 请求 + Cookie: auth_session=xxx │
│ table查找Session → 获取用户信息 → 处理请求 │
└─────────────────────────────────────────────────────────┘
6. 为什么不能只用一种?
只用JWT Token的问题
问题1:性能开销
-
每次请求都要解析JWT
-
解析需要计算签名,性能开销大
问题2:无法主动注销
-
Token在过期前一直有效
-
即使知道Token泄露,也无法立即失效
只用Session的问题
问题1:跨服务认证
-
Session无法跨服务使用
-
table服务无法验证wflow服务的Session
问题2:扩展性
-
多服务器需要共享Session
-
需要Redis/数据库存储
混合方案的优势
优势1:解决跨服务认证
- JWT Token可以跨服务使用
优势2:性能优化
- 后续请求用Session,性能好
优势3:可控性
- 可以主动注销Session
总结
1. 有状态 vs 无状态
- 有状态 = 服务器需要存储信息(Session)
- 无状态 = 服务器不需要存储信息(JWT Token)
2. SessionID 安全性
- SessionID是随机生成的,几乎不可能被猜中
- 即使猜中,也有多层保护(过期时间、IP绑定等)
- 使用HTTPS、HttpOnly、Secure等安全措施
3. 混合场景
- 先用JWT Token(跨服务认证)
- 再换成Session Cookie(性能优化)
- 结合两者优势,解决实际问题