长短 Token 安全认证设计总结
长短 Token 安全认证设计总结
1. 核心概念
| Token 类型 | 用途 | 生命周期 | 存储方式 | 安全要求 |
| ------------------------ | ------------ | -------------- | ------------------------ | ---------------- |
| 短 Token (Access Token) | 接口鉴权 | 短(如15分钟) | 前端内存/LocalStorage | 中等(需防泄露) |
| 长 Token (Refresh Token) | 刷新短 Token | 长(如7天) | HttpOnly Cookie 或数据库 | 高(需严格保护) |
2. 核心流程
2.1 用户登录认证
http POST /login HTTP/1.1 Content-Type: application/json { "username": "user", "password": "pass" }
http HTTP/1.1 200 OK Set-Cookie: refresh_token=abc123xyz; HttpOnly; Secure; SameSite=Strict Content-Type: application/json { "access_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.xxxxx", "expires_in": 900 }
2.2 短 Token 刷新流程
http POST /refresh-token HTTP/1.1 Cookie: refresh_token=abc123xyz // 浏览器自动携带 HttpOnly Cookie
http HTTP/1.1 200 OK Set-Cookie: refresh_token=new_abc123xyz; HttpOnly; Secure; SameSite=Strict Content-Type: application/json { "access_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.new_token", "expires_in": 900 }
3. 安全措施
3.1 长 Token 存储
-
数据库存储:
-
存储长 Token 的哈希值(如 bcrypt 或 sha256),避免明文存储。
-
绑定用户 ID、设备指纹(IP + User-Agent 哈希)、过期时间。
-
示例 SQL:
sql CREATE TABLE refresh_tokens ( id SERIAL PRIMARY KEY, user_id INT NOT NULL, token_hash TEXT NOT NULL, -- 哈希后的长 Token fingerprint TEXT, -- 设备指纹 expires_at TIMESTAMP, created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP );
3.2 长 Token 传输
-
HttpOnly Cookie:
-
通过 Set-Cookie 头部传输,防止 XSS 攻击。
-
设置 Secure(仅 HTTPS)、SameSite=Strict(防止 CSRF)。
3.3 Token 旋转(Rotation)
-
每次刷新生成新长 Token:
-
旧长 Token 立即失效,防止长期泄露。
-
示例逻辑:
javascript // 服务端生成新长 Token 并更新数据库 const newRefreshToken = crypto.randomBytes(64).toString('hex'); const hashedToken = hash(newRefreshToken); db.query( 'UPDATE refresh_tokens SET token_hash = $1, updated_at = NOW() WHERE user_id = $2', [hashedToken, userId] );
3.4 设备指纹绑定
-
验证设备信息:
-
检查长 Token 是否与当前设备指纹(IP + User-Agent)匹配。
-
示例代码:
javascript function getFingerprint(req) { return crypto.createHash('sha256') .update(req.ip + req.headers['user-agent']) .digest('hex'); } const storedFingerprint = db.getFingerprint(refreshToken); if (storedFingerprint !== getFingerprint(req)) { throw new Error('Invalid device fingerprint'); }
3.5 短 Token 黑名单(可选)
-
短期吊销:
-
短 Token 过期后加入 Redis 黑名单,防止重复使用。
-
示例:
javascript // Redis 缓存短 Token 黑名单(TTL = 短 Token 剩余时间) redis.setex(oldAccessToken, remainingTTL, 'revoked');
4. 代码示例(Node.js + JWT)
4.1 生成 Token
javascript const jwt = require('jsonwebtoken'); const crypto = require('crypto'); // 生成短 Token function generateAccessToken(userId, permissions) { return jwt.sign( { user_id: userId, permissions }, process.env.JWT_SECRET, { expiresIn: '15m' } ); } // 生成长 Token function generateRefreshToken() { return crypto.randomBytes(64).toString('hex'); }
4.2 刷新 Token 接口
javascript app.post('/refresh-token', (req, res) => { const refreshToken = req.cookies.refresh_token; if (!refreshToken) return res.status(403).send('Missing refresh token'); // 1. 验证长 Token 是否有效 const hashedToken = hash(refreshToken); const tokenRecord = db.query( 'SELECT * FROM refresh_tokens WHERE token_hash = $1', [hashedToken] ); if (!tokenRecord || tokenRecord.expires_at < new Date()) { return res.status(403).send('Invalid refresh token'); } // 2. 生成新短 Token const newAccessToken = generateAccessToken(tokenRecord.user_id, ['read']); // 3. 可选:生成新长 Token(旋转机制) const newRefreshToken = generateRefreshToken(); db.query( 'UPDATE refresh_tokens SET token_hash = $1 WHERE user_id = $2', [hash(newRefreshToken), tokenRecord.user_id] ); // 4. 返回新短 Token res.cookie('refresh_token', newRefreshToken, { httpOnly: true, secure: true }); res.json({ access_token: newAccessToken }); });
5. 常见问题
5.1 为什么不能一起返回长短 Token?
-
风险:长 Token 泄露可能导致长期账户控制。
-
正确做法:长 Token 通过 HttpOnly Cookie 传输,短 Token 通过 JSON 响应返回。
5.2 长 Token 被盗用怎么办?
-
检测异常:绑定设备指纹,发现异常 IP/User-Agent 时强制登出。
-
立即吊销:删除数据库中的长 Token,并将短 Token 加入黑名单。
5.3 用户主动登出如何处理?
-
服务端:删除数据库中的长 Token。
-
客户端:清除本地存储的短 Token 和 Cookie。
6. 总结
-
短 Token:无状态 JWT,存储在前端,用于接口鉴权。
-
长 Token:存储在数据库,通过 HttpOnly Cookie 传输,用于刷新短 Token。
-
核心安全原则:
-
长 Token 必须严格保护(哈希存储 + HttpOnly Cookie)。
-
绑定设备指纹 + Token 旋转机制。
-
短 Token 黑名单(可选)。
通过分层保护,确保即使短 Token 泄露,攻击者也无法长期控制用户账户。
本文由萧兮的博客原创发布,欢迎转载,转载务必保留原文链接。
萧兮的博客:https://www.20010515.xyz · 原文:https://www.20010515.xyz/posts/b8a88f23-ab4a-4d44-973b-85fe46fea9b3