JWT 和 传统session登录的区别

传统登录方式 session模式 (有状态 - Stateful)

验证用户登录成功后,会生成一个唯一的key,key对应的value存着用户的信息。然后把key返回给浏览器(cookie)作为登录凭证

Session ID之所以不会被伪造,核心原因在于:

它被设计成一个通过密码学级别的随机算法生成的、足够长的、无意义的字符串,这使得通过猜测或计算来命中一个有效ID的概率小到了在现实世界中可以忽略不计的程度。

缺点:

  • 当用户量很大的时候,单台服务压力很大,需要的内存也很多。如果服务器挂了,整个登录服务就挂了。
  • 微服务架构,

有状态(Session) :需要专门的存储资源(内存、Redis等)来维护数百万甚至上亿用户的Session数据。这既是存储成本,也是I/O开销

JWT机制(无状态 - Stateless)

无状态"就是服务器在处理请求时,不需要依赖之前请求的状态。每次请求都像第一次见面一样,所有需要的信息都由客户端在本次请求中提供

  1. 登录: 用户提交用户名和密码。
  2. 服务器: 验证通过后,生成一个包含用户标识和权限信息的 JWT。这个 JWT 经过了签名,但服务器不存储这个 JWT
  3. 响应: 服务器将这个 JWT 直接返回给客户端。
  4. 客户端存储: 客户端(如浏览器)自己负责存储这个 JWT,通常放在 localStorage、sessionStorage 或 HTTP Header(Authorization 字段)中。
  5. 后续请求: 客户端在后续的每次请求中,都需要在 Authorization 请求头里附带上这个 JWT。
    Authorization: Bearer
  6. 服务器验证: 服务器收到请求后,从请求头中取出 JWT。它不需要查找任何存储,只需用自己的密钥验证 JWT 的签名是否有效。如果签名有效,服务器就可以信任 Payload 中的信息,从而确定用户的身份和权限

JWT优点

  1. 无状态和可扩展性 (Stateless & Scalable): 这是最大的优势。因为服务器不存储 Session,所以应用可以轻松地进行水平扩展。任何一台服务器都可以处理来自任何用户的请求,只要它们共享相同的密钥。这对于构建分布式系统和微服务架构非常有利。
  2. 跨域认证 (Cross-Domain Authentication): 传统的 Cookie 存在跨域问题(同源策略限制)。而 JWT 因为通常放在 Authorization 请求头中,天然不存在跨域问题,非常适合用于分离的前后端架构(如 SPA + API)或为多个不同的服务提供统一认证。
  3. 自包含性 (Self-Contained): JWT 的 Payload 中可以包含用户的基本信息(如用户ID、角色),服务端在验证签名后,可以直接从 Payload 中获取这些信息,避免了频繁查询数据库,减轻了数据库的压力。
  4. 适用于多种终端 (Versatile): 不仅限于 Web 应用,JWT 同样适用于移动端(iOS, Android)、桌面应用等,因为其认证方式不依赖于 Cookie。
  5. 解耦 (Decoupling): 认证服务器和业务服务器可以解耦。认证服务器只负责生成和签发 Token,业务服务器只需验证 Token 的有效性即可,职责清晰。

JWT缺点

  1. 无法主动失效 (Cannot be Invalidated Actively): 这是最大的缺点。一旦 JWT 被签发,在它的过期时间(exp)到达之前,它就一直是有效的。如果用户在此期间退出登录或被管理员禁用,服务器无法立即让这个 JWT 失效。
  • 解决方案:

    • 设置较短的过期时间: 配合 Refresh Token 机制来获取新的 JWT。(实践中双token比较多短时效Access Token + 长时效Refresh Token" 是一种在安全性和用户体验之间取得精妙平衡的行业标准实践。它承认了泄露的可能性,并通过限制时间来将风险控制在可接受的范围内
    • 维护一个黑名单 (Blacklist): 将需要失效的 JWT 存入 Redis 或数据库中。每次验证时,先检查该 JWT 是否在黑名单内。但这又破坏了 JWT 的无状态性
  1. 安全性问题 (Security Issues):

    • Payload 明文: Payload 仅是 Base64 编码,不是加密。敏感信息绝不能存放。
    • 令牌泄露: 如果 JWT 被截获(例如通过 XSS 攻击),攻击者就可以在有效期内冒充用户身份。因此,推荐使用 HTTPS 来加密通信。将 JWT 存储在 HttpOnly 的 Cookie 中可以有效防止 XSS 攻击,但这又会引入 CSRF 风险(需要配合 SameSite 等策略来缓解)。
  2. 令牌体积较大 (Larger Size): 由于包含了 Header 和 Payload 信息,JWT 通常比一个简单的 Session ID 要大得多。每次请求都携带它,会增加网络传输的开销。

  3. 续签问题复杂 (Renewal Complexity): 如果 JWT 过期时间设置得较短,用户在使用过程中 Token 可能会过期,导致体验不佳。通常需要引入一套"Refresh Token"机制来自动续签,这增加了系统的复杂性。Refresh Token 本身需要安全地存储,并且也需要处理其失效和轮换的逻辑。

JWT的工作原理

一、签名的本质

JWT 的签名(Signature)是这样算出来的(以 HS256 为例):

scss 复制代码
signature = HMACSHA256(
  base64UrlEncode(header) + "." + base64UrlEncode(payload),
  secret
)

也就是说:

  • 签名是由 header + payload + secret 三者共同决定的;

  • 其中 secret 是 服务器端私有的(攻击者拿不到)。

    非对称签名 非对称的原理就是(用公钥验证这个签名是不是 用私钥签发的,(私钥保密的,所以如果是私钥签发的,说明信息是可以相信的))

非对称签名用 一对密钥私钥(private key)公钥(public key)

  • 签名(Sign) :签发方用 私钥 对消息的哈希值做签名,得到签名值(signature)。私钥必须保密。
    常见流程:digest = Hash(message)signature = Sign_with_private_key(digest)
  • 验证(Verify) :验证方用 公钥 验证签名是否对应该消息(即验证签名对 digest 是否成立)。
    验证成功 → 消息确实由持有私钥的一方签发且在传输中未被篡改。

注意:签名不是加密。签名保证完整性不可否认性/来源证明(在非对称场景下),消息主体通常仍是明文可读的。 # 对称签名 ## 🧩 一、基本概念

  • 对称签名算法 :指签名与验证都使用同一把密钥(secret)。
  • 常见算法:HS256HS384HS512,即基于 HMAC(Hash-based Message Authentication Code) 的算法。

所以:签名者和验证者共享同一个 secret。只要知道这个 secret,任何人都能签名和验证。


⚙️ 二、签名(生成 Token)过程

假设要生成一个 JWT,算法为 HS256

1️⃣ 准备三部分

makefile 复制代码
header = {
  "alg": "HS256",
  "typ": "JWT"
}

payload = {
  "sub": "1234567890",
  "name": "John Doe",
  "iat": 1690000000
}
secret = "my-very-secret-key"

2️⃣ Base64URL 编码 header 和 payload

ini 复制代码
base64Header = base64urlEncode(header)
base64Payload = base64urlEncode(payload)

3️⃣ 拼接待签名字符串

ini 复制代码
message = base64Header + "." + base64Payload

4️⃣ 用 HMAC-SHA256 算法签名

ini 复制代码
signature = HMACSHA256(message, secret)

计算逻辑:

scss 复制代码
HMAC = Hash( (secret ⊕ opad) + Hash( (secret ⊕ ipad) + message ) )

HMAC 的原理是:

  • secret 做两次 hash 混合(内层 + 外层),
  • 这样可以避免"长度扩展攻击",
  • 最终输出一个定长的 hash(32字节)。

5️⃣ 再 base64URL 编码签名

ini 复制代码
base64Signature = base64urlEncode(signature)

6️⃣ 拼出最终 JWT

ini 复制代码
jwt = base64Header + "." + base64Payload + "." + base64Signature

✅ 完成签名!服务端通常把这个 token 发给客户端。


🔍 三、验证(认证)过程

当客户端带着 JWT 来访问接口时,比如:

makefile 复制代码
Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...

服务端验证步骤如下:

1️⃣ 拆解 token

ini 复制代码
token = "header.payload.signature"
[b64Header, b64Payload, b64Signature] = token.split('.')

2️⃣ Base64URL 解码前两段(header、payload)

ini 复制代码
header = JSON.parse(base64urlDecode(b64Header))
payload = JSON.parse(base64urlDecode(b64Payload))

3️⃣ (可选)检查 header.alg 是否在允许列表中

不要盲信 header.alg,而是检查它是否在白名单,例如:

javascript 复制代码
if (header.alg !== 'HS256') throw new Error('不支持该算法');

4️⃣ 用同一个 secret 重新计算签名

ini 复制代码
expectedSig = base64urlEncode( HMACSHA256(b64Header + "." + b64Payload, secret) )

5️⃣ 比较签名

常数时间比较(timing-safe compare)

javascript 复制代码
if (expectedSig !== b64Signature) throw new Error('签名不匹配');

6️⃣ 验证 payload 的有效性

diff 复制代码
- exp(过期时间)> 当前时间?
- nbf(不可用时间)< 当前时间?
- iss / aud / sub 等字段是否匹配?

✅ 通过验证后:

服务器就认为这个 JWT 是由自己签发的合法 token,可以信任其内容。


🔐 四、签名与认证关系图

scss 复制代码
[签发方]                           [验证方]
   ↓                                   ↓
header + payload                  token(header.payload.signature)
   ↓                                   ↓
HMAC(header.payload, secret)      HMAC(header.payload, secret)
   ↓                                   ↓
 signatureA                          signatureB
   ↓                                   ↓
  比较:signatureA === signatureB  ✅ → 有效

两边都用同一个 secret,如果结果一样,说明 token 未被篡改。


⚠️ 五、安全要点(很关键)

  1. secret 不能泄露:一旦泄露,攻击者可以自己生成合法 token!
  2. 不要信任 header.alg:服务端必须强制指定算法,比如只允许 HS256。
  3. 签名前不要重新 JSON.stringify :要用原始的 base64url 字符串拼接,否则签名会不一致。
  4. 注意 timing attack :用 crypto.timingSafeEqual() 等方法比较签名。
  5. 密钥长度HS256 的 secret 建议至少 32 字节以上。
  6. 密钥轮换 :定期更换 secret,可通过 kid(key id) 管理版本。
相关推荐
我是苏苏1 小时前
Web开发:C#通过ProcessStartInfo动态调用执行Python脚本
java·服务器·前端
无羡仙1 小时前
Vue插槽
前端·vue.js
用户6387994773052 小时前
每组件(Per-Component)与集中式(Centralized)i18n
前端·javascript
SsunmdayKT2 小时前
React + Ts eslint配置
前端
开始学java2 小时前
useEffect 空依赖 + 定时器 = 闭包陷阱?count 永远停在 1 的坑我踩透了
前端
zerosrat2 小时前
从零实现 React Native(2): 跨平台支持
前端·react native
狗哥哥2 小时前
🔥 Vue 3 项目深度优化之旅:从 787KB 到极致性能
前端·vue.js
青莲8432 小时前
RecyclerView 完全指南
android·前端·面试
青莲8432 小时前
Android WebView 混合开发完整指南
android·前端·面试
GIS之路2 小时前
GDAL 实现矢量数据转换处理(全)
前端