引言
在 Web 开发中,Cookie、Session 和 JWT 是三种常见的身份认证和状态管理方式。它们在用户身份验证、会话管理以及跨域访问等方面扮演着重要角色。然而,不同的机制有不同的特点、适用场景和安全风险。
一、认证、授权和凭证
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,这样就维持了用户的登录状态。
3.2 Session
- 流程
前端发起登录请求,将用户名密码传给服务端,服务端在数据库查询进行用户名密码的认证,如果认证成功,就可以往session当中存入当前的用户信息,然后进行响应,会在响应头里存入一个set-cookie的属性,然后再把当前session的唯一ID放在属性当中,前端会自动在cookie当中存入当前的session ID,登录成功之后,下一次的请求就会自动在请求头当中设置cookie的信息,服务端拿到cookie中的session ID就可以得到这一次的请求所对应的session信息了,那么就可以获取到当前登录的用户信息,以维持当前登录状态。
3.3 JWT
- 流程
前端发起登录请求,将用户名密码传给服务端,服务端在数据库查询进行用户名密码的认证,如果认证成功,服务端会生成一个JWT token(包含一些用户的基本非敏感信息,用户ID和用户名等)并返回给前端,前端保存这个token,在后续请求接口时在header中添加Authorization字段,并且把token放在该字段的value中,服务端通过header获取token之后,校验token的签名是否有效,若有效且token没有过期,则通过payload获取用户信息,以维持当前登录状态。
JWT 的原理
JWT(JSON Web Token)是一种紧凑且自包含的令牌格式,通常用于在客户端和服务器之间安全地传递信息。它的结构分为三部分,格式为:xxx.xxx.xxx
,分别对应Header、Payload和Signature。
1. Header(头部)
- 内容:
alg
:签名算法,例如HS256(HMAC SHA-256)。typ
:令牌类型,固定为JWT
。
- 特点:
- 经过Base64编码,公开可见。
- 用于描述令牌的元数据和签名算法。
示例:
{
"alg": "HS256",
"typ": "JWT"
}
2. Payload(荷载)
- 内容:
- 包含用户的声明(claims),例如:
sub
:用户ID(subject)。name
:用户名。exp
:过期时间(expiration time)。- 其他自定义字段(如角色、权限等)。
- 包含用户的声明(claims),例如:
- 特点:
- 经过Base64编码,公开可见。
- 存储非敏感数据,因为可以被解码查看。
示例:
{
"sub": "1234567890",
"name": "John Doe",
"exp": 1516239022
}
3. Signature(签名)
- 生成方式:
- 将Header和Payload分别进行Base64编码,得到两个字符串。
- 将这两个字符串用
.
拼接起来,形成base64UrlEncode(Header) + "." + base64UrlEncode(Payload)
。 - 使用Header中指定的签名算法(如HS256)和密钥(
secret_key
)对拼接后的字符串进行签名,生成签名部分。
- 作用:
- 用于验证令牌的完整性和真实性,防止数据被篡改。
签名公式:
Signature = HMACSHA256(
base64UrlEncode(Header) + "." + base64UrlEncode(Payload),
secret_key
)
4. JWT 的验证过程
- 拆分Token:
- 将JWT按
.
拆分为三部分:Header、Payload和Signature。
- 将JWT按
- 解码Header和Payload:
- 对Header和Payload进行Base64解码,获取原始数据。
- 重新生成签名:
- 使用相同的密钥(
secret_key
)和签名算法,对解码后的Header和Payload重新生成签名。
- 使用相同的密钥(
- 比对签名:
- 将重新生成的签名与JWT中的Signature部分进行比对。
- 如果一致,说明Token未被篡改,Payload中的数据可信。
- 如果不一致,说明Token可能被篡改,验证失败。
5. JWT 的特点
- 优点:
- 自包含:所有信息都存储在Token中,无需额外查询数据库。
- 无状态:服务端无需保存会话信息,适合分布式系统。
- 安全性:通过签名防止数据被篡改。
- 缺点:
- Token一旦签发,在有效期内无法直接失效(需借助黑名单等机制)。
- 如果未使用HTTPS,Token可能被窃取。
四、cookie、session和jwt的区别
4.1 存储位置
- Cookie:数据存储在客户端(浏览器),每次请求时都会自动携带到服务器。
- Session:数据存储在服务器,客户端通过
sessionId
访问服务器上的数据。 - JWT:数据存储在客户端(一般保存在
localStorage
、sessionStorage
或cookie
),服务器只负责验证,不存储用户状态。
4.2 安全性
-
Cookie:
- 如果未设置
HttpOnly
,JavaScript 可以访问,容易受到 XSS(跨站脚本攻击)。 - 如果未设置
Secure
,在 HTTP 连接中可能被劫持(中间人攻击)。
- 如果未设置
-
Session:
- 服务器存储 Session 数据,较 Cookie 更安全。
- 需要 Session 机制配合
cookie
或token
进行身份识别。
-
JWT:
- 采用签名(HMAC 或 RSA)保证数据完整性,但不能防止被窃取。
- 一旦被拦截,由于 JWT 本身包含所有信息,可能导致更严重的数据泄露问题。
4.3 数据存储
- Cookie:一般存储少量数据(4KB 限制)。
- Session:存储在服务器,理论上可以存储大量数据,但会占用服务器资源。
- JWT:存储在客户端,大小一般受限于
localStorage
或cookie
,但通常比 Cookie 能存储更多信息。
4.4 适用场景
-
Cookie:
- 适用于存储少量的、对安全性要求不高的数据,如用户偏好设置。
- 可用于轻量级的身份认证(如
remember me
功能)。
-
Session:
- 适用于需要服务器存储用户信息的场景,如购物车、用户登录状态管理。
- 适用于需要更高安全性的应用。
-
JWT:
- 适用于无状态认证(Stateless Authentication),如单点登录(SSO)。
- 适用于微服务架构,因为不需要服务器存储用户状态信息。
4.5 是否支持跨域
- Cookie:默认不支持跨域访问,但可以通过
SameSite=None
和CORS
配置来实现跨域。 - Session:依赖 Cookie 机制,默认不能跨域。
- JWT:可以在多个服务器或前后端分离的架构中使用,前提是正确配置 CORS。
4.6 是否支持无状态
- Cookie:有状态(由服务器验证)。
- Session:有状态(需要服务器存储)。
- JWT:无状态(无需服务器存储,只需要验证签名)。
4.7 优缺点对比
特性 | Cookie | Session | JWT |
---|---|---|---|
存储位置 | 客户端 | 服务器 | 客户端 |
安全性 | 较低 | 较高 | 取决于存储方式 |
服务器压力 | 无 | 高 | 无 |
适用场景 | 轻量级存储 | 需要存储用户状态 | 无状态认证、微服务 |
是否支持跨域 | 受限 | 受限 | 支持 |
是否可篡改 | 易被篡改 | 不易篡改 | 签名验证防篡改 |
适合单点登录(SSO) | 否 | 否 | 是 |
五、注意事项
5.1 使用 Cookie 时需要注意
-
安全配置
-
设置
HttpOnly
,防止 JavaScript 访问,避免 XSS 攻击。 -
设置
Secure
,确保 Cookie 仅在 HTTPS 连接下传输,防止中间人攻击。 -
使用
SameSite
限制 Cookie 发送行为,防止 CSRF 攻击:Strict
:不允许跨站请求携带 Cookie(最安全)。Lax
(默认):允许部分跨站请求(如 GET)。None
(需要Secure
):允许所有跨站请求,适用于跨域场景。
-
-
存储敏感信息
- 避免存储用户密码等敏感信息,建议只存储
token
或sessionId
。 - 若存储
JWT
,建议设置较短的httpOnly
失效时间,并配合refresh token
。
- 避免存储用户密码等敏感信息,建议只存储
-
Cookie 大小限制
- 单个 Cookie 最大 4KB,存储过多数据会导致请求变大,影响性能。
-
Cookie 过期时间
-
使用
Expires
或Max-Age
控制过期时间:Session Cookie
(不设置过期时间):浏览器关闭时删除。Persistent Cookie
(设置Max-Age
):可持久化存储。
-
-
跨域问题
- 通过
Access-Control-Allow-Credentials: true
允许跨域携带 Cookie,但要确保 CORS 服务器正确配置。
- 通过
5.2 使用 Session 时需要注意
-
存储方式
- 默认存储在服务器内存中,适用于小规模应用,但大规模应用建议使用 Redis 或 数据库 存储,提高扩展性。
- 如果 Session 过多,会占用服务器资源,影响性能。
-
Session ID 传输
- 通常通过 Cookie 传递
sessionId
,但也可以通过 URL 传递(不推荐,容易被劫持)。 - 使用
Secure
和HttpOnly
保护存储sessionId
的 Cookie,防止 XSS 和劫持攻击。
- 通常通过 Cookie 传递
-
Session 过期策略
-
设定合理的过期时间,避免长期占用服务器资源:
- 短期会话:如 30 分钟内无操作自动销毁。
- 长期登录:可以存储
Remember Me
机制,让用户保持登录状态。
-
-
分布式应用
-
在多服务器环境下,Session 需要做 共享存储,避免用户在不同服务器登录时丢失 Session。
-
方案:
- Redis/Memcached:将 Session 存储到缓存,提高查询效率。
- 数据库:适用于持久化存储 Session,但查询性能不如缓存。
-
-
Session 固定攻击(Session Fixation)
-
避免用户在认证前后使用相同的 Session ID,建议:
- 在登录成功后,重新生成 Session ID。
- 结合 IP 地址、User-Agent 进行身份校验,防止 Session 被盗用。
-
5.3 使用 JWT 时需要注意
-
避免 JWT 过长
- JWT 由
Header.Payload.Signature
组成,存储过多数据会导致请求体积变大,影响性能。 - 建议只存储必要信息(如用户 ID、角色权限),避免存储敏感数据。
- JWT 由
-
Token 过期策略
-
JWT 一旦生成,服务器无法主动销毁(除非存储黑名单)。
-
解决方案:
- 短期 Token(Access Token) :设置较短过期时间(如 15~30 分钟)。
- 长期 Token(Refresh Token) :存储在数据库,用于刷新 Access Token,降低频繁登录的用户体验问题。
-
-
存储位置
- Cookie(推荐) :搭配
HttpOnly
和Secure
提高安全性。 - localStorage / sessionStorage(不推荐) :容易被 XSS 攻击获取,增加被盗风险。
- Cookie(推荐) :搭配
-
避免 Token 泄露
-
一旦 Token 被窃取,攻击者可随意使用,因此:
- 使用 HTTPS 传输 Token,防止中间人攻击。
- 不在 URL 传递 Token(容易被日志或浏览器缓存暴露)。
- 可结合 指纹识别(如 IP 绑定、设备 ID) 限制 Token 使用范围。
-
-
Token 失效处理
-
JWT 无法主动撤销,因此可以:
- 黑名单机制:存储废弃 Token 列表,但需要占用服务器资源。
- 数据库存储 Token:在用户登出时,使数据库中该 Token 失效。
-
-
签名算法选择
-
避免使用对称加密(如
HS256
),尽量使用 非对称加密(如RS256
) ,提高安全性:- HS256(HMAC) :密钥泄露后攻击者可伪造 Token。
- RS256(RSA) :公钥验证,私钥签名,更安全。
-
-
防止重放攻击
- 使用
jti
(JWT ID) 或nonce
(唯一随机数)确保 Token 不能被重复使用。 - 结合时间戳
iat
和exp
,拒绝过期或重复请求。
- 使用
结语
无论是 Cookie、Session 还是 JWT,它们各有优劣,并没有绝对的“最佳方案”,而是需要根据业务需求和安全策略做出合理选择。对于一般的 Web 应用,Session 适用于服务器端身份管理,JWT 更适合分布式和无状态认证,而 Cookie 则在存储少量信息时更为便捷。在实际开发中,除了选择合适的方案,还需要注意安全性,避免常见攻击风险,如 XSS、CSRF、Session 盗用 和 Token 泄露,以确保应用的稳定性和数据安全。