Web身份认证与状态管理:Cookie、Session 与 JWT

670 阅读13分钟

引言

在 Web 开发中,CookieSessionJWT 是三种常见的身份认证和状态管理方式。它们在用户身份验证、会话管理以及跨域访问等方面扮演着重要角色。然而,不同的机制有不同的特点、适用场景和安全风险。

image.png

一、认证、授权和凭证

1.1 认证的定义

  • 简单来说,认证就是确认用户的真实身份,确保“你就是你”。

  • 在网络环境中,认证通常通过以下几种方式实现:

    • 使用用户名和密码登录
    • 通过邮箱发送登录链接
    • 通过手机接收验证码
    • 只要能接收到邮箱或验证码,系统就默认你是账户的合法拥有者

1.2 授权的定义

  • 授权是指用户允许第三方应用访问其特定资源的权限

    • 例如,在安装手机应用时,应用会请求访问相册、地理位置等权限
    • 在登录微信小程序时,小程序会请求获取昵称、头像、地区、性别等个人信息
  • 常见的授权实现方式包括:cookie、session和token

1.3 凭证的定义

  • 凭证是实现认证和授权的基础,它是一种身份标识(证书),用于标记访问者的身份。

二、什么是Cookie、Session和JWT

2.1 Cookie

  • 定义:Cookie是服务器发送到用户浏览器并保存在本地的一小块数据。
  • 作用
    • 用于在客户端存储用户的会话信息或状态。
    • 每次浏览器向服务器发送请求时,会自动携带Cookie,从而实现身份验证或状态保持。
  • 特点
    • 存储在客户端(浏览器)。
    • 有大小限制(通常为4KB)。
    • 可以设置过期时间,分为会话Cookie(关闭浏览器后失效)和持久Cookie(根据过期时间失效)。
  • 应用场景
    • 保存用户登录状态。
    • 记录用户的偏好设置(如语言、主题)。

2.2 Session

  • 定义:Session是服务器端存储用户会话信息的一种机制。
  • 作用
    • 用于在服务器端保存用户的会话数据,通常与Cookie配合使用。
    • 服务器会为每个用户创建一个唯一的Session ID,并通过Cookie传递给客户端。
  • 特点
    • 存储在服务器端,安全性较高。
    • 依赖于Cookie来传递Session ID。
    • 会话结束后(如用户关闭浏览器),Session数据可能会被清除(取决于服务器配置)。
  • 应用场景
    • 保存用户的登录状态。
    • 存储用户的临时数据(如购物车信息)。

2.3 JWT(JSON Web Token)

  • 定义:JWT是一种开放标准(RFC 7519),用于在各方之间安全地传输信息作为JSON对象。
  • 作用
    • 用于在客户端和服务器之间传递认证信息或声明。
    • 通常用于无状态的分布式系统中。
  • 结构
    • Header:包含令牌类型和签名算法(如HMAC SHA256)。
    • Payload:包含声明(如用户ID、角色、过期时间等)。
    • Signature:用于验证令牌的完整性和真实性。
  • 特点
    • 存储在客户端(如LocalStorage或Cookie)。
    • 自包含性:所有信息都存储在令牌中,无需额外查询数据库。
    • 无状态性:服务器无需保存会话信息,适合分布式系统。
  • 应用场景
    • 用户认证与授权。
    • 跨域身份验证(如单点登录SSO)。
    • API接口的安全验证。

三、它们是如何维持用户登录状态的

3.1 Cookie

  • 流程

前端发起登录请求,将用户名密码传给服务端,服务端在数据库查询进行用户名密码的认证,如果认证成功,会响应用户名到前端,前端把用户名保存到cookie,cookie保存在浏览器这一端,登录成功之后的所有请求都会自动带上这个cookie,这样就维持了用户的登录状态。

image.png

3.2 Session

  • 流程

前端发起登录请求,将用户名密码传给服务端,服务端在数据库查询进行用户名密码的认证,如果认证成功,就可以往session当中存入当前的用户信息,然后进行响应,会在响应头里存入一个set-cookie的属性,然后再把当前session的唯一ID放在属性当中,前端会自动在cookie当中存入当前的session ID,登录成功之后,下一次的请求就会自动在请求头当中设置cookie的信息,服务端拿到cookie中的session ID就可以得到这一次的请求所对应的session信息了,那么就可以获取到当前登录的用户信息,以维持当前登录状态。

image.png

3.3 JWT

  • 流程

前端发起登录请求,将用户名密码传给服务端,服务端在数据库查询进行用户名密码的认证,如果认证成功,服务端会生成一个JWT token(包含一些用户的基本非敏感信息,用户ID和用户名等)并返回给前端,前端保存这个token,在后续请求接口时在header中添加Authorization字段,并且把token放在该字段的value中,服务端通过header获取token之后,校验token的签名是否有效,若有效且token没有过期,则通过payload获取用户信息,以维持当前登录状态。

JWT 的原理

JWT(JSON Web Token)是一种紧凑且自包含的令牌格式,通常用于在客户端和服务器之间安全地传递信息。它的结构分为三部分,格式为:xxx.xxx.xxx,分别对应HeaderPayloadSignature

1. Header(头部)

  • 内容
    • alg:签名算法,例如HS256(HMAC SHA-256)。
    • typ:令牌类型,固定为JWT
  • 特点
    • 经过Base64编码,公开可见。
    • 用于描述令牌的元数据和签名算法。

示例

{
  "alg": "HS256",
  "typ": "JWT"
}

2. Payload(荷载)

  • 内容
    • 包含用户的声明(claims),例如:
      • sub:用户ID(subject)。
      • name:用户名。
      • exp:过期时间(expiration time)。
      • 其他自定义字段(如角色、权限等)。
  • 特点
    • 经过Base64编码,公开可见。
    • 存储非敏感数据,因为可以被解码查看。

示例

{
  "sub": "1234567890",
  "name": "John Doe",
  "exp": 1516239022
}

3. Signature(签名)

  • 生成方式
    1. 将Header和Payload分别进行Base64编码,得到两个字符串。
    2. 将这两个字符串用.拼接起来,形成base64UrlEncode(Header) + "." + base64UrlEncode(Payload)
    3. 使用Header中指定的签名算法(如HS256)和密钥(secret_key)对拼接后的字符串进行签名,生成签名部分。
  • 作用
    • 用于验证令牌的完整性和真实性,防止数据被篡改。

签名公式

Signature = HMACSHA256(
  base64UrlEncode(Header) + "." + base64UrlEncode(Payload),
  secret_key
)

4. JWT 的验证过程

  1. 拆分Token
    • 将JWT按.拆分为三部分:Header、Payload和Signature。
  2. 解码Header和Payload
    • 对Header和Payload进行Base64解码,获取原始数据。
  3. 重新生成签名
    • 使用相同的密钥(secret_key)和签名算法,对解码后的Header和Payload重新生成签名。
  4. 比对签名
    • 将重新生成的签名与JWT中的Signature部分进行比对。
    • 如果一致,说明Token未被篡改,Payload中的数据可信。
    • 如果不一致,说明Token可能被篡改,验证失败。

5. JWT 的特点

  • 优点
    • 自包含:所有信息都存储在Token中,无需额外查询数据库。
    • 无状态:服务端无需保存会话信息,适合分布式系统。
    • 安全性:通过签名防止数据被篡改。
  • 缺点
    • Token一旦签发,在有效期内无法直接失效(需借助黑名单等机制)。
    • 如果未使用HTTPS,Token可能被窃取。

四、cookie、session和jwt的区别

4.1 存储位置

  • Cookie:数据存储在客户端(浏览器),每次请求时都会自动携带到服务器。
  • Session:数据存储在服务器,客户端通过 sessionId 访问服务器上的数据。
  • JWT:数据存储在客户端(一般保存在 localStoragesessionStoragecookie),服务器只负责验证,不存储用户状态。

4.2 安全性

  • Cookie

    • 如果未设置 HttpOnly,JavaScript 可以访问,容易受到 XSS(跨站脚本攻击)。
    • 如果未设置 Secure,在 HTTP 连接中可能被劫持(中间人攻击)。
  • Session

    • 服务器存储 Session 数据,较 Cookie 更安全。
    • 需要 Session 机制配合 cookietoken 进行身份识别。
  • JWT

    • 采用签名(HMAC 或 RSA)保证数据完整性,但不能防止被窃取。
    • 一旦被拦截,由于 JWT 本身包含所有信息,可能导致更严重的数据泄露问题。

4.3 数据存储

  • Cookie:一般存储少量数据(4KB 限制)。
  • Session:存储在服务器,理论上可以存储大量数据,但会占用服务器资源。
  • JWT:存储在客户端,大小一般受限于 localStoragecookie,但通常比 Cookie 能存储更多信息。

4.4 适用场景

  • Cookie

    • 适用于存储少量的、对安全性要求不高的数据,如用户偏好设置。
    • 可用于轻量级的身份认证(如 remember me 功能)。
  • Session

    • 适用于需要服务器存储用户信息的场景,如购物车、用户登录状态管理。
    • 适用于需要更高安全性的应用。
  • JWT

    • 适用于无状态认证(Stateless Authentication),如单点登录(SSO)。
    • 适用于微服务架构,因为不需要服务器存储用户状态信息。

4.5 是否支持跨域

  • Cookie:默认不支持跨域访问,但可以通过 SameSite=NoneCORS 配置来实现跨域。
  • Session:依赖 Cookie 机制,默认不能跨域。
  • JWT:可以在多个服务器或前后端分离的架构中使用,前提是正确配置 CORS。

4.6 是否支持无状态

  • Cookie:有状态(由服务器验证)。
  • Session:有状态(需要服务器存储)。
  • JWT:无状态(无需服务器存储,只需要验证签名)。

4.7 优缺点对比

特性CookieSessionJWT
存储位置客户端服务器客户端
安全性较低较高取决于存储方式
服务器压力
适用场景轻量级存储需要存储用户状态无状态认证、微服务
是否支持跨域受限受限支持
是否可篡改易被篡改不易篡改签名验证防篡改
适合单点登录(SSO)

五、注意事项

5.1 使用 Cookie 时需要注意

  1. 安全配置

    • 设置 HttpOnly,防止 JavaScript 访问,避免 XSS 攻击。

    • 设置 Secure,确保 Cookie 仅在 HTTPS 连接下传输,防止中间人攻击。

    • 使用 SameSite 限制 Cookie 发送行为,防止 CSRF 攻击:

      • Strict:不允许跨站请求携带 Cookie(最安全)。
      • Lax(默认):允许部分跨站请求(如 GET)。
      • None(需要 Secure):允许所有跨站请求,适用于跨域场景。
  2. 存储敏感信息

    • 避免存储用户密码等敏感信息,建议只存储 tokensessionId
    • 若存储 JWT,建议设置较短的 httpOnly 失效时间,并配合 refresh token
  3. Cookie 大小限制

    • 单个 Cookie 最大 4KB,存储过多数据会导致请求变大,影响性能。
  4. Cookie 过期时间

    • 使用 ExpiresMax-Age 控制过期时间:

      • Session Cookie(不设置过期时间):浏览器关闭时删除。
      • Persistent Cookie(设置 Max-Age):可持久化存储。
  5. 跨域问题

    • 通过 Access-Control-Allow-Credentials: true 允许跨域携带 Cookie,但要确保 CORS 服务器正确配置。

5.2 使用 Session 时需要注意

  1. 存储方式

    • 默认存储在服务器内存中,适用于小规模应用,但大规模应用建议使用 Redis数据库 存储,提高扩展性。
    • 如果 Session 过多,会占用服务器资源,影响性能。
  2. Session ID 传输

    • 通常通过 Cookie 传递 sessionId,但也可以通过 URL 传递(不推荐,容易被劫持)。
    • 使用 SecureHttpOnly 保护存储 sessionId 的 Cookie,防止 XSS 和劫持攻击。
  3. Session 过期策略

    • 设定合理的过期时间,避免长期占用服务器资源:

      • 短期会话:如 30 分钟内无操作自动销毁。
      • 长期登录:可以存储 Remember Me 机制,让用户保持登录状态。
  4. 分布式应用

    • 在多服务器环境下,Session 需要做 共享存储,避免用户在不同服务器登录时丢失 Session。

    • 方案:

      • Redis/Memcached:将 Session 存储到缓存,提高查询效率。
      • 数据库:适用于持久化存储 Session,但查询性能不如缓存。
  5. Session 固定攻击(Session Fixation)

    • 避免用户在认证前后使用相同的 Session ID,建议:

      • 在登录成功后,重新生成 Session ID
      • 结合 IP 地址、User-Agent 进行身份校验,防止 Session 被盗用。

5.3 使用 JWT 时需要注意

  1. 避免 JWT 过长

    • JWT 由 Header.Payload.Signature 组成,存储过多数据会导致请求体积变大,影响性能。
    • 建议只存储必要信息(如用户 ID、角色权限),避免存储敏感数据。
  2. Token 过期策略

    • JWT 一旦生成,服务器无法主动销毁(除非存储黑名单)。

    • 解决方案:

      • 短期 Token(Access Token) :设置较短过期时间(如 15~30 分钟)。
      • 长期 Token(Refresh Token) :存储在数据库,用于刷新 Access Token,降低频繁登录的用户体验问题。
  3. 存储位置

    • Cookie(推荐) :搭配 HttpOnlySecure 提高安全性。
    • localStorage / sessionStorage(不推荐) :容易被 XSS 攻击获取,增加被盗风险。
  4. 避免 Token 泄露

    • 一旦 Token 被窃取,攻击者可随意使用,因此:

      • 使用 HTTPS 传输 Token,防止中间人攻击。
      • 不在 URL 传递 Token(容易被日志或浏览器缓存暴露)。
      • 可结合 指纹识别(如 IP 绑定、设备 ID) 限制 Token 使用范围。
  5. Token 失效处理

    • JWT 无法主动撤销,因此可以:

      • 黑名单机制:存储废弃 Token 列表,但需要占用服务器资源。
      • 数据库存储 Token:在用户登出时,使数据库中该 Token 失效。
  6. 签名算法选择

    • 避免使用对称加密(如 HS256),尽量使用 非对称加密(如 RS256 ,提高安全性:

      • HS256(HMAC) :密钥泄露后攻击者可伪造 Token。
      • RS256(RSA) :公钥验证,私钥签名,更安全。
  7. 防止重放攻击

    • 使用 jti(JWT ID) 或 nonce(唯一随机数)确保 Token 不能被重复使用。
    • 结合时间戳 iatexp,拒绝过期或重复请求。

结语

无论是 CookieSession 还是 JWT,它们各有优劣,并没有绝对的“最佳方案”,而是需要根据业务需求和安全策略做出合理选择。对于一般的 Web 应用,Session 适用于服务器端身份管理JWT 更适合分布式和无状态认证,而 Cookie 则在存储少量信息时更为便捷。在实际开发中,除了选择合适的方案,还需要注意安全性,避免常见攻击风险,如 XSS、CSRF、Session 盗用 和 Token 泄露,以确保应用的稳定性和数据安全。