核心概念:JWT 是一种“令牌”格式,而不是一种“存储或传输机制”
JWT 本身只是一种紧凑且自包含的方式,用于在各方之间安全地传输信息。它定义了令牌的结构(Header.Payload.Signature),并包含验证令牌完整性(未被篡改)和真实性(确实由已知方签发)的方法。
JWT 不关心你如何存储或传输它。 令牌本身可以放在:
- HTTP 请求头 (Authorization: Bearer
) -
URL 查询参数
-
请求体
-
Cookie
所以我选择把 JWT 放在了 Cookie 里面,通过 res.cookie 的设置,可以很方便地设置 Cookie 自携带,并且可以一定程度避免 XSS 攻击。
res.cookie('authToken', token, {
httpOnly: true,
secure: process.env.NODE_ENV === 'production',
maxAge: 14400000,
sameSite: 'Lax', // 或 'None'
});
这条代码的作用就是告诉浏览器:“嘿,我这里有一个 authToken 叫这个值,请把它存起来,并在将来发送到 localhost:3000 的请求中。”
- 浏览器自动管理 HTTP-only Cookie。
- 一旦 Cookie 被设置,浏览器会负责在后续发送到同一域 (或兼容域/路径) 的请求中,自动将其包含在 Request Headers 的 Cookie 字段中。你的前端 JavaScript 代码无需手动读取 localStorage 或 sessionStorage 并将其添加到每个请求头中。
- 后端通过 cookie-parser 中间件解析这些 Cookie。
- 当请求到达后端时,cookie-parser 中间件会解析 Request Headers 中的 Cookie 字段,并将这些 Cookie 的键值对填充到 req.cookies 对象中。
所以,当你在 /login 路由或 authenticateJWT 中间件中执行 const token = req.cookies.authToken; 时,你实际上是在从浏览器自动发送过来的 Cookie 中获取 JWT。
XSS 和 CSRF
业界普遍认为 XSS 攻击的危害性更大,因为它可以做的事情更多(窃取所有可访问的数据,甚至在用户会话中执行任意操作)。因此,很多应用会选择将 Token 存储在 HttpOnly 的 Cookie 中来防御 XSS,然后通过 SameSite 属性(Lax 或 Strict)或配合 CSRF Token 来防御 CSRF。
所以:
在 JWT 的认证体系中:
- 将 JWT 存储在 localStorage 或 sessionStorage,并通过 Authorization 请求头发送:
- 优点: 天然防御 CSRF。
-
缺点: 极易受到 XSS 攻击,因为恶意 JavaScript 可以直接读取 Token。
-
将 JWT 存储在 HttpOnly 的 Cookie 中:
- 优点: 有效防御 XSS 攻击(因为 JavaScript 无法访问 HttpOnly Cookie)。
-
缺点: 易受 CSRF 攻击(如果 SameSite 设置不当或没有其他 CSRF Token 机制),因为浏览器会自动携带 Cookie。
所以,当你回过神的时候,有没有发现,这个博文的封面,其实是 Session Vs Token …
发表回复