JWT 究竟是什么
JWT(JSON Web Token)是一种紧凑且 URL 安全的声明传输方式,用于在两方之间传递声明。它不是魔法般的安全解决方案,而是一种令牌格式:由三个 base64url 编码的段组成,以点号分隔(header.payload.signature)。
header: {"alg":"HS256","typ":"JWT"}
payload: {"sub":"user123","iat":1700000000}
signature: HMACSHA256(base64url(header)+"."+base64url(payload), secret)
Enter fullscreen mode Exit fullscreen mode
常见错误:将 JWT 存储在 localStorage 中
不要这样做。localStorage 可通过 JavaScript 访问(XSS)。如果攻击者注入脚本,就会窃取你的令牌。改用 httpOnly cookie。cookie 会随请求自动发送,且无法被 JS 读取。
// Bad: vulnerable to XSS
localStorage.setItem('token', jwt);
// Better: httpOnly cookie (set by server)
Set-Cookie: token=<jwt>; HttpOnly; Secure; SameSite=Strict
Enter fullscreen mode Exit fullscreen mode
如何实现 JWT 认证(实用方法)
1. 登录端点在 cookie 中签发 JWT
app.post('/login', (req, res) => {
const { username, password } = req.body;
const user = authenticate(username, password);
if (!user) return res.status(401).send('Invalid credentials');
const token = jwt.sign(
{ sub: user.id, role: user.role },
process.env.JWT_SECRET,
{ expiresIn: '15m' }
);
res.cookie('token', token, {
httpOnly: true,
secure: true,
sameSite: 'strict',
maxAge: 15 * 60 * 1000
});
res.json({ message: 'Logged in' });
});
Enter fullscreen mode Exit fullscreen mode
2. 中间件在每次请求时验证令牌
function authMiddleware(req, res, next) {
const token = req.cookies.token;
if (!token) return res.status(401).send('No token');
try {
const decoded = jwt.verify(token, process.env.JWT_SECRET);
req.user = decoded;
next();
} catch (err) {
return res.status(403).send('Invalid token');
}
}
app.get('/protected', authMiddleware, (req, res) => {
res.json({ user: req.user });
});
Enter fullscreen mode Exit fullscreen mode
3. 使用刷新令牌实现更长会话
访问令牌有效期较短(15 分钟)。使用刷新令牌(长期有效,存储在单独的 httpOnly cookie 中)来获取新的访问令牌,而无需再次输入凭据。
app.post('/refresh', (req, res) => {
const refreshToken = req.cookies.refreshToken;
if (!refreshToken) return res.status(401).send('No refresh token');
try {
const decoded = jwt.verify(refreshToken, process.env.REFRESH_SECRET);
const newAccessToken = jwt.sign(
{ sub: decoded.sub },
process.env.JWT_SECRET,
{ expiresIn: '15m' }
);
res.cookie('token', newAccessToken, { httpOnly: true, secure: true, sameSite: 'strict' });
res.json({ message: 'Token refreshed' });
} catch (err) {
return res.status(403).send('Invalid refresh token');
}
});
Enter fullscreen mode Exit fullscreen mode
JWT 无法解决的问题
- CSRF:如果使用 cookie,需要 CSRF 防护。可使用 SameSite=Strict 或 CSRF 令牌。
- 令牌吊销:JWT 是无状态的。除非维护黑名单(这会破坏无状态性),否则无法吊销单个令牌。登出时,可在服务端清除 cookie 或使用较短的过期时间。
- 载荷篡改:签名可确保完整性。未验证签名前,切勿信任载荷。
常见陷阱
- 在代码中存储密钥:使用环境变量。
-
使用弱密钥:使用长且随机的字符串(例如
openssl rand -hex 64)。 - 未验证算法:如果服务器接受 'none' 算法,攻击者即可伪造令牌。请始终指定允许的算法。
// Explicitly set algorithms
jwt.verify(token, secret, { algorithms: ['HS256'] });
Enter fullscreen mode Exit fullscreen mode
总结
JWT 是一种工具,而非万能解决方案。请使用 httpOnly cookie、短生命周期令牌、刷新令牌以及适当的 CSRF 防护。理解权衡:无状态性与吊销能力。保持简单。
0 Comments
Log in to join the conversation.No comments yet. Be the first to share your thoughts.