Skip to content

从Cookie到SessionId再到JWT的演变与对比

约 2090 字大约 7 分钟

JWT面试

2025-07-30

一、Cookie的诞生与原理

早期Web是无状态的,服务器无法识别用户身份。为了解决这个问题,Cookie应运而生。Cookie是浏览器存储在本地的小型文本数据,每次请求都会自动携带到服务器。

Cookie特点:

  • 由浏览器自动管理和发送
  • 可设置过期时间、作用域、安全属性
  • 容量有限(一般4KB)

典型应用: 用于保存用户偏好、登录状态、跟踪信息等。

二、Session与SessionId

Cookie虽然能存储信息,但不安全且容量有限。于是,Session机制出现。Session在服务器端保存用户数据,客户端只保存一个SessionId(通常通过Cookie传递)。

SessionId流程:

  1. 用户登录,服务器生成Session并返回SessionId给客户端。
  2. 客户端通过Cookie保存SessionId。
  3. 每次请求时,浏览器自动携带SessionId,服务器根据SessionId查找用户数据。

Session优点:

  • 数据存储在服务器,安全性高
  • 可存储大量数据

Session缺点:

  • 服务器需维护大量Session,消耗内存
  • 分布式部署时需做Session同步

三、JWT(JSON Web Token)

随着前后端分离和分布式架构流行,JWT成为主流认证方式。JWT是一种自包含的令牌,所有信息都在Token中,服务器无需保存会话。

JWT结构详解:

  1. Header(头部)

    • 通常是一个JSON对象,描述令牌类型(typ,通常为JWT)和所用的签名算法(alg,如HS256、RS256)。
    • 示例:
      {
        "alg": "HS256",
        "typ": "JWT"
      }
  2. Payload(负载)

    • 也是一个JSON对象,包含实际要传递的数据(Claims,声明)。常见声明有:
      • iss(签发者)、exp(过期时间)、sub(主题)、aud(受众)、iat(签发时间)、nbf(生效时间)、jti(唯一ID)等。
      • 也可以自定义字段,如用户ID、角色等。
    • 示例:
      {
        "sub": "1234567890",
        "name": "John Doe",
        "admin": true,
        "exp": 1717171717
      }
  3. Signature(签名)

    • 用于验证令牌的完整性和真实性。
    • 生成方式:
      HMACSHA256(
        base64UrlEncode(header) + "." + base64UrlEncode(payload),
        secret)
    • 服务器用密钥对Header和Payload进行签名,客户端和服务器都可用密钥验证签名是否被篡改。

JWT的格式如下:

header.payload.signature

JWT实际示例

假设有如下JWT:

eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VyX2lkIjo2MTYyMDg5ODU2OTk1MzI4LCJ1c2VybmFtZSI6ImJiIiwiaXNzIjoic21hcnRtZW1vIiwiZXhwIjoxNzgwODI0OTMzfQ.teBlE6RxxDVdCE7SptrljFKScPMBTpCa2CmPNS_mXgA

其中:

  • Header(头部,Base64Url解码后):
    {"alg":"HS256","typ":"JWT"}
  • Payload(负载,Base64Url解码后):
    {
      "user_id": 6162089856995328,
      "username": "bb",
      "iss": "smartmemo",
      "exp": 1780824933
    }
  • Signature(签名): 使用HS256算法,密钥(加盐字符串)为“夏天夏天悄悄过去”,对header和payload进行签名生成。

签名过程:

HMACSHA256(
  base64UrlEncode(header) + "." + base64UrlEncode(payload),
  "夏天夏天悄悄过去"
)

这样,只有知道密钥“夏天夏天悄悄过去”的服务端才能验证和生成该JWT。

为什么密钥能防止内容被篡改?

JWT的签名部分是用密钥(如“夏天夏天悄悄过去”)对Header和Payload进行加密哈希(如HS256)生成的。每次服务端收到JWT后,会用同样的密钥重新计算签名:

  1. 先Base64Url解码Header和Payload。
  2. 用密钥和算法重新生成签名。
  3. 与JWT中的Signature部分进行比对。

如果内容被篡改(如Payload被修改),重新计算出的签名就和原始签名不一致,服务端会拒绝该JWT。

因此,只有知道密钥的服务端才能生成和验证JWT,保证内容未被篡改。

为什么不能通过修改payload直接登录其他账号?

有些人可能会尝试将JWT的payload部分(如user_id或username)解码后修改为其他账号的信息,然后重新编码,企图伪造登录。

但实际上,这样做无法通过服务端校验:

  1. JWT的签名是基于原始header和payload、密钥共同生成的。
  2. 如果你修改了payload内容(如user_id),但没有密钥,无法重新生成正确的签名。
  3. 服务端收到JWT后,会用密钥重新计算签名,与JWT中的signature比对。
  4. 签名不一致,服务端会拒绝该请求。

因此,只有拥有密钥的服务端才能生成有效的JWT,用户无法通过简单修改payload来冒充其他账号。

JWT流程:

  1. 用户登录后,服务器生成JWT并返回给客户端。
  2. 客户端将JWT存储(如localStorage或cookie)。
  3. 客户端每次请求时,将JWT放在HTTP头部(如Authorization)。
  4. 服务器验证JWT的签名和有效性,确认用户身份。

JWT优点:

四、JWT常见问题与最佳实践

1. 为什么JWT可以被解析?

JWT的Header和Payload部分只是Base64Url编码(不是加密),任何人都可以解码查看内容,但无法验证真伪或伪造签名,除非知道密钥。

2. JWT的校验过程(后端做的事)

  • 解码Header和Payload
  • 使用服务端密钥重新计算签名
  • 与Token中的Signature对比
  • 一致则信任,不一致则拒绝
  • 同时校验exp(过期)、iat(签发时间)等字段

3. JWT中不应该包含哪些信息?

  • ❌ 明文密码、身份证、手机号等敏感信息
  • ❌ 不能让前端或中间人看到的隐私字段
  • ✅ 推荐只放非敏感声明信息,如user_id、username、role、exp、iat、iss等
  • 如果需要敏感信息,应在服务端查询或用加密方式(如JWE)处理

4. 最佳实践建议

场景是否推荐说明
在JWT中放user_id或role安全,常见做法
在JWT中放邮箱/用户名⚠️可放,但注意隐私
在JWT中放密码/手机号/敏感数据不推荐,存在泄露风险
只放user_id,其他信息通过服务端查安全且便于控制

如果你后续打算用JWT设计用户系统(如登录、权限、接口鉴权),建议只在JWT中存放必要且非敏感的信息,其他敏感数据通过服务端查询。

  • 安全性高,通过签名防止数据篡改

JWT缺点:

  • 无法主动失效,除非令牌过期
  • Payload部分为明文,敏感信息不应存储其中

四、三者对比

特性CookieSessionIdJWT
存储位置客户端客户端+服务器客户端
安全性较低较高依赖签名
扩展性受限服务器内存受限易于分布式扩展
数据容量小(4KB)大(服务器端)适中(Token体积)
主动失效可设置可销毁需额外机制
分布式支持一般需同步天然支持

五、总结

Cookie、SessionId和JWT是Web认证与状态管理的三种主要方式。Cookie适合简单场景,SessionId适合安全性要求高的传统架构,JWT则更适合分布式和前后端分离的现代应用。实际选择需结合业务需求、安全性和系统架构。


面试口语化总结

在现代的 web 服务里,通常需要进行身份校验以访问一些特定资源。早期做法是直接在 cookie 里存储校验信息,比如用户 id 或 token,但 cookie 容量有限,而且明文存储用户信息存在安全隐患。

后来引入了 sessionID,服务端生成 session 并返回 sessionID,客户端通过 cookie 保存 sessionID,每次请求带上 sessionID,服务端查找对应的用户信息。sessionID 解决了明文暴露的问题,但也有缺点,比如服务器要维护大量 session,分布式部署时还要做 session 同步。

现在更常用的是 JWT token 校验。相比 cookie 和 session,JWT 的优点是无状态,服务器不用保存会话信息,天然支持分布式和前后端分离。

JWT token 由三部分组成:Header、Payload 和 Signature。Header 和 Payload 是明文存储的,Signature 是用 header、payload 和服务器密钥一起生成的。

校验时,服务端会用密钥重新计算签名,和 token 里的 signature 比对。如果有人拿到 a 用户的 token,修改 payload 里的 user_id 试图冒充 b 用户,因为没有密钥,无法生成正确的签名,服务端校验不通过。

所以 JWT 能防止信息篡改和冒名访问,但也不建议在 payload 里存储敏感信息。