Node.js 实战三:Token 认证、Session 管理与中间件设计实战
当你的接口开始需要「登录态」,你会遇到这些问题:
-
登录后怎么保持用户状态?用 Cookie 还是 Token?
-
Token 要放哪?Header、Cookie、LocalStorage?
-
如何处理鉴权失败、自动过期、权限控制?
-
每个接口都手动判断用户?会不会太麻烦?
别急,这一篇,我们就来一次性讲清楚登录认证体系、Session 管理与中间件设计的完整套路。
一、前端认证的两种主流方式
模式 | 技术关键词 | 适用场景 |
---|---|---|
Session + Cookie | 服务端记录状态,前端自动带 cookie | 同源、传统 Web 网站 |
Token (JWT) | 客户端存储 token,接口用 header 传 | 多端、前后端分离、跨域系统 |
区别:
项目 | Cookie-Session | Token-JWT |
---|---|---|
状态存储 | 服务端 | 客户端 |
跨域 | 不方便 | 灵活支持 |
安全性 | 默认防重放 | 易泄漏(需搭配策略) |
可扩展 | 支持撤销、手动失效 | 更适合无状态服务 |
我们推荐在中后台/多端系统中优先使用 Token 模式(JWT)。
二、使用 JWT 实现登录认证
安装依赖:
npm i jsonwebtoken koa-jwt
生成 Token:
const jwt = require('jsonwebtoken');const token = jwt.sign({ id: user.id }, 'secret-key', { expiresIn: '2h' });
验证 Token 中间件:
const jwtMiddleware = require('koa-jwt');app.use(jwtMiddleware({ secret: 'secret-key' }).unless({path: [/^\/public/, /^\/login/], // 免鉴权路由
}));
token 应该放在 Authorization header 中:
Authorization: Bearer xxx.token.yyy
三、如何统一封装鉴权中间件?
你可以封装一个权限控制中间件,例如:
const auth = () => async (ctx, next) => {try {if (!ctx.state.user) ctx.throw(401, '未登录');await next();} catch (err) {ctx.status = err.status || 401;ctx.body = { error: '认证失败,请重新登录' };}
};
然后在受保护接口中使用:
router.get('/user/profile', auth(), async (ctx) => {ctx.body = { user: ctx.state.user };
});
四、权限控制的“粒度”该怎么设计?
-
用户登录 ≠ 拥有操作权限
-
推荐添加角色字段 / 权限字段:
const user = { id: 1, role: 'admin', permissions: ['read', 'edit'] };
-
可封装权限判断器:
const permit = (permission) => async (ctx, next) => {const perms = ctx.state.user?.permissions || [];if (!perms.includes(permission)) ctx.throw(403, '无操作权限');await next();
};
五、Token 失效、续期、登出如何处理?
问题 | 解决方式 |
---|---|
Token 到期 | expiresIn 控制过期时间 |
提前续期 | 后端提供 /refresh-token 接口 |
主动退出 | 客户端删除 token |
后台踢人 | 维护黑名单机制或使用 Redis session 状态控制 |
如果对安全性要求高,建议使用:
-
短效 Access Token + 长效 Refresh Token
-
Refresh Token 存储在 Cookie 中,Access Token 用于接口调用
六、是否应该将 Token 放在 Cookie?
可以(尤其是搭配 HttpOnly + Secure)
注意设置跨域 SameSite=None; Secure 属性,否则可能无法带上 Cookie
小结:
位置 | 安全性 | 灵活性 |
---|---|---|
LocalStorage | 简单但易被 XSS 攻击 | 高 |
Cookie(HttpOnly) | 较安全(防 XSS) | 配置复杂 |
七、中间件的“组合拳”:验证、解析、权限、异常
构建完整认证体系的中间件链:
app.use(errorHandler()); // 全局错误捕获
app.use(jwtValidator()); // 鉴权处理
app.use(requestLogger()); // 日志记录
app.use(ctxUserExtractor()); // ctx.state.user 注入
中间件就是你的“安全战队”,合理组合能让你少写一堆 if。
总结:认证不是登录按钮,它是一套系统
你要解决的不只是“谁登录了”,而是:
-
登录了谁?能干啥?能干多久?出了问题怎么办?
-
是否可以安全、统一、可维护地进行用户身份验证?
Token 模式 + 中间件体系 + 合理封装,
是你走出“demo 项目”迈向“稳定系统”的关键一步。