token机制
Token(令牌)是前后端身份验证的核心机制,用于在用户登录后证明其身份,避免频繁输入账号密码。其运行流程可分为生成、传输、验证、失效四个阶段,以下是详细解析:
一、Token 的核心作用
- 身份凭证:用户登录成功后,服务器生成一个唯一 Token,作为用户后续请求的 “通行证”。
- 无状态验证:服务器无需存储用户会话信息(区别于传统的 Session 机制),仅通过 Token 即可验证身份,适合分布式系统。
二、完整运行流程
1. 生成 Token(登录阶段)
-
用户提交凭证:用户在登录页面输入账号密码(或其他验证信息,如手机号验证码),前端将数据发送到后端登录接口。
-
服务器验证并生成 Token:
-
后端验证账号密码的有效性。
-
验证通过后,服务器根据用户信息(如用户 ID、角色、权限等)生成 Token。
-
Token 通常通过加密算法(如 JWT 的 HS256、RS256)生成,包含三部分:
- Header(头部):指定加密算法(如
{"alg": "HS256", "typ": "JWT"})。 - Payload(载荷):存储用户核心信息(如
{"userId": 123, "role": "admin", "exp": 1620000000},exp为过期时间)。 - Signature(签名):用服务器密钥对 Header 和 Payload 加密后的字符串,确保 Token 未被篡改。
- Header(头部):指定加密算法(如
-
服务器将生成的 Token 返回给前端。
// 后端(Node.js示例,使用jsonwebtoken库) const jwt = require('jsonwebtoken'); const secret = 'server-secret-key'; // 服务器私有密钥 // 登录接口 app.post('/login', (req, res) => { const { username, password } = req.body; // 假设验证通过,获取用户信息 const user = { id: 1, username: 'admin', role: 'admin' }; // 生成Token,设置2小时后过期 const token = jwt.sign( { userId: user.id, role: user.role }, secret, { expiresIn: '2h' } ); res.json({ code: 200, token }); // 返回Token给前端 }); -
2. 存储 Token(前端)
前端收到 Token 后,需存储在本地,常用方式:
-
localStorage:持久化存储(关闭浏览器不丢失,适合长期登录),但存在 XSS 攻击风险。
-
sessionStorage:会话级存储(关闭浏览器后删除,适合临时登录)。
-
HTTP-only Cookie:通过后端设置
HttpOnly: true,避免 JS 访问,防御 XSS 攻击,同时可设置Secure: true(仅 HTTPS 传输)和SameSite(防御 CSRF)。// 前端登录成功后存储Token axios.post('/login', { username, password }).then(res => { const { token } = res.data; localStorage.setItem('token', token); // 存储到localStorage });
3. 传输 Token(请求阶段)
用户后续访问需要权限的接口时,前端需在请求中携带 Token,主流方式:
-
HTTP 请求头(推荐):通过
Authorization字段携带,格式通常为Bearer <token>。 -
URL 参数:(不推荐,存在安全风险,易暴露在日志中)。
// 前端请求拦截器(Axios示例) axios.interceptors.request.use(config => { const token = localStorage.getItem('token'); if (token) { config.headers.Authorization = `Bearer ${token}`; // 添加到请求头 } return config; });
4. 验证 Token(服务器)
-
服务器解析 Token:后端接口收到请求后,从
Authorization头中提取 Token。 -
验证 Token 有效性:
- 检查签名是否正确(用服务器密钥验证,确保未被篡改)。
- 检查是否过期(验证
exp字段)。 - 解析 Payload 中的用户信息(如用户 ID、角色),确认用户有权限访问接口。
-
处理验证结果:
- 验证通过:正常处理请求,返回数据。
- 验证失败(如 Token 无效、过期):返回 401(未授权)或 403(禁止访问),前端跳转至登录页。
// 后端验证中间件(Node.js示例) const authMiddleware = (req, res, next) => { const authHeader = req.headers.authorization; if (!authHeader || !authHeader.startsWith('Bearer ')) { return res.status(401).json({ message: '未提供Token' }); } const token = authHeader.split(' ')[1]; try { // 验证Token并解析用户信息 const decoded = jwt.verify(token, secret); req.user = decoded; // 将用户信息挂载到请求对象 next(); // 验证通过,继续处理请求 } catch (err) { return res.status(401).json({ message: 'Token无效或已过期' }); } }; // 保护需要权限的接口 app.get('/user/info', authMiddleware, (req, res) => { // 使用解析出的用户信息处理请求 res.json({ userId: req.user.userId, role: req.user.role }); });
5. Token 失效与刷新
-
主动失效:用户退出登录时,前端删除本地 Token,后端可通过 “黑名单” 机制(如 Redis 存储失效 Token)强制使已颁发的 Token 失效(适用于安全场景,如账号被盗)。
-
过期刷新:为避免用户频繁登录,可采用 “双 Token 机制”:
- Access Token:短期有效(如 2 小时),用于接口访问。
- Refresh Token:长期有效(如 7 天),用于 Access Token 过期后获取新的 Access Token。
- 流程:Access Token 过期时,前端用 Refresh Token 调用刷新接口,获取新的 Access Token,继续使用。
三、Token 的优势与风险
优势:
- 无状态:服务器无需存储会话,减轻服务器压力,适合分布式系统。
- 跨域支持:相比 Cookie,Token 在跨域请求中更易处理(如前后端分离架构)。
- 扩展性:可在 Payload 中携带用户角色、权限等信息,减少数据库查询。
风险:
- XSS 攻击:若 Token 存储在 localStorage,可能被恶意脚本窃取(需配合 HTTPS、CSP 策略防御)。
- 无法即时回收:Token 一旦颁发,在过期前始终有效(除非用黑名单机制)。
- Payload 可解码:JWT 的 Payload 是 Base64 编码(非加密),不可存储敏感信息(如密码)。
总结
Token 的运行核心是 “一次验证,多次使用”:用户登录时生成加密 Token,后续请求通过 Token 证明身份,服务器通过解密和签名验证确认合法性。实际应用中需结合存储方式(如 HTTP-only Cookie)、刷新机制(如双 Token)和安全策略(如 HTTPS),平衡安全性和用户体验。
分割线
session机制
传统的 Session(会话)机制是早期 Web 开发中实现用户身份认证的主流方式,核心是通过服务器存储用户状态来维持登录会话。其运行流程围绕会话创建、状态存储、身份验证三个核心环节,以下是详细解析:
一、Session 的核心原理
Session 机制基于 “服务器存储用户状态” 的思想:用户登录后,服务器为其创建一个唯一的会话(Session),并通过一个标识(Session ID)与用户绑定。后续请求中,用户通过携带 Session ID 证明身份,服务器通过 ID 查找对应的会话状态,实现身份验证。
二、完整运行流程
1. 会话创建(登录阶段)
-
用户提交凭证:用户在登录页面输入账号密码,前端将数据发送到后端登录接口。
-
服务器验证并创建 Session:
- 后端验证账号密码有效性。
- 验证通过后,服务器创建一个Session 对象(存储在服务器内存、数据库或缓存中),包含用户信息(如用户 ID、角色、登录时间等)。
- 服务器生成一个唯一的Session ID(通常是随机字符串,如
sess_abc123xyz),作为该 Session 的标识。 - 服务器将 Session ID 发送给前端(通常通过 Cookie 传递)。
// 后端(Node.js + Express 示例) const express = require('express'); const session = require('express-session'); const app = express(); // 配置 Session 存储(默认存内存,生产环境用 Redis 等) app.use(session({ secret: 'server-secret', // 用于加密 Session ID 的密钥 resave: false, saveUninitialized: true, cookie: { secure: false } // 开发环境关闭 HTTPS 要求 })); // 登录接口 app.post('/login', (req, res) => { const { username, password } = req.body; // 假设验证通过,获取用户信息 const user = { id: 1, username: 'admin' }; // 将用户信息存入 Session(服务器端) req.session.user = user; // Session ID 会自动通过 Cookie 发送给前端 res.json({ code: 200, message: '登录成功' }); });
2. 存储 Session ID(前端)
前端收到服务器返回的 Session ID 后,通常通过 Cookie 自动存储(由浏览器管理):
-
服务器在响应头中设置
Set-Cookie字段,例如:
Set-Cookie: connect.sid=sess_abc123xyz; Path=/; HttpOnly; Max-Age=86400 -
浏览器会将该 Cookie 保存,并在后续请求中自动携带(无需前端手动处理)。
// 响应头示例(服务器 → 浏览器) HTTP/1.1 200 OK Set-Cookie: connect.sid=sess_abc123xyz; Path=/; HttpOnly Content-Type: application/json
3. 身份验证(请求阶段)
用户访问需要权限的接口时,浏览器自动携带 Session ID(通过 Cookie),服务器通过 ID 验证身份:
-
前端发送请求:请求头中自动包含 Session ID 的 Cookie,例如:
Cookie: connect.sid=sess_abc123xyz -
服务器验证 Session:
- 服务器从请求 Cookie 中提取 Session ID。
- 根据 ID 查找对应的 Session 对象(如从内存、Redis 中读取)。
- 若找到 Session 且状态有效(未过期),则确认用户身份,允许访问接口。
- 若未找到 Session(如 ID 无效、已过期),则拒绝访问,要求重新登录。
// 后端验证接口(需登录才能访问) app.get('/user/info', (req, res) => { // 从 Session 中获取用户信息(服务器已通过 Session ID 找到对应 Session) if (req.session.user) { // 验证通过,返回用户信息 res.json({ user: req.session.user }); } else { // 未登录,返回 401 错误 res.status(401).json({ message: '请先登录' }); } });
4. 会话销毁(退出登录)
-
主动退出:用户点击退出登录时,前端发送退出请求,服务器销毁对应的 Session 对象,并清除 Session ID 对应的 Cookie。
-
自动过期:服务器为 Session 设置过期时间(如 2 小时),过期后自动销毁,用户需重新登录。
// 退出登录接口 app.post('/logout', (req, res) => { // 销毁当前 Session req.session.destroy(err => { if (err) return res.status(500).json({ message: '退出失败' }); // 清除客户端 Cookie res.clearCookie('connect.sid'); res.json({ code: 200, message: '退出成功' }); }); });
三、Session 的存储方式
服务器存储 Session 的位置决定了其扩展性和可靠性:
- 内存存储:默认方式,优点是快,缺点是服务器重启后 Session 丢失,且不支持分布式系统(多服务器间无法共享 Session)。
- 文件存储:将 Session 写入服务器文件,解决内存存储的丢失问题,但性能较差。
- 数据库存储:如 MySQL、MongoDB,适合需要持久化的场景,但读写性能一般。
- 缓存存储:如 Redis、Memcached,性能优异且支持分布式(多服务器可共享 Redis 中的 Session),是生产环境的主流选择。
四、Session 与 Token 的核心区别
| 对比维度 | Session 机制 | Token 机制 |
|---|---|---|
| 状态存储位置 | 服务器端(内存 / 数据库 / Redis) | 客户端(localStorage/Cookie) |
| 验证方式 | 通过 Session ID 查服务器状态 | 直接解析 Token 验证签名 |
| 扩展性 | 分布式系统需共享 Session 存储 | 无状态,天然支持分布式 |
| 跨域支持 | 受 Cookie 跨域限制 | 无限制,可在请求头中传递 |
| 安全性依赖 | 依赖 Cookie 安全配置 | 依赖 Token 加密和传输安全 |
总结
Session 机制的核心是 “服务器保存状态,客户端携带 ID”:通过 Session ID 关联用户与服务器端的会话状态,实现身份验证。其优点是实现简单、安全性较高(依赖服务器存储),但在分布式系统中需要额外配置共享存储(如 Redis)。随着前后端分离和跨域场景增多,Token 机制逐渐成为主流,但 Session 仍在传统 Web 应用中广泛使用。



1355

被折叠的 条评论
为什么被折叠?



