📝 JWT 学习与实战小结
一、概念篇
-
JWT 是什么
-
JSON Web Token(JWT)是一种自包含的 token,用于客户端与服务端之间安全传递信息。
-
JWT 本质是一个字符串,包含三部分:
- Header(头部):声明算法等信息
- Payload(载荷):存储用户信息等数据
- Signature(签名):用 secret 和 Header+Payload 生成,保证内容不被篡改
-
-
Session vs Token
特性 Session JWT/Token 存储位置 服务器 客户端(localStorage/cookie) 扩展性 单机/分布式需共享 session 分布式无需共享状态 验证方式 服务器查 session 服务器验签 JWT 安全性 服务器掌控 依赖 secret + 签名算法 -
Token 使用原理
- 用户登录 → 后端生成 JWT → 返回给前端
- 前端保存 token(localStorage/sessionStorage)
- 每次请求受保护接口 → 带上 token → 后端验签
二、后端篇(Egg.js)
-
安装插件
npm install egg-jwt -
配置 secret
// config/config.default.js exports.jwt = { secret: 'my_jwt_secret_key_123456' }; -
中间件
// app/middleware/jwtAuth.js module.exports = () => { return async function jwtAuth(ctx, next) { const token = ctx.request.header.authorization; if (!token) { ctx.status = 401; ctx.body = { code: 401, message: '未携带 token' }; return; } try { const tokenValue = token.replace('Bearer ', ''); ctx.state.user = ctx.app.jwt.verify(tokenValue, ctx.app.config.jwt.secret); await next(); } catch (err) { ctx.status = 401; ctx.body = { code: 401, message: 'token 无效或已过期' }; } }; }; -
路由注册
const jwtAuth = app.middleware.jwtAuth(); router.post('/login', controller.auth.login); router.get('/user/info', jwtAuth, controller.user.info); -
Controller 生成 JWT
const token = app.jwt.sign({ username }, app.config.jwt.secret, { expiresIn: '1h' });
三、前端基础篇(Vue 3)
-
登录表单
- 用户输入用户名/密码 → 调用
/login→ 拿 token - 保存 token:
localStorage.setItem('jwt_token', token)
- 用户输入用户名/密码 → 调用
-
Axios 拦截器
// 每次请求自动带 token service.interceptors.request.use(config => { const token = localStorage.getItem('jwt_token'); if (token && config.headers) config.headers['Authorization'] = `Bearer ${token}`; return config; }); // 处理全局 401 service.interceptors.response.use( response => response, error => { if (error.response?.status === 401) { alert('登录已过期,请重新登录'); localStorage.removeItem('jwt_token'); window.location.href = '/login'; } return Promise.reject(error); } ); -
为什么不用 Vue 响应式变量保存 token
- 页面刷新后内存清空 → token 消失
- localStorage 持久化存储,刷新页面仍可读取
四、前端进阶篇
-
路由守卫
meta.requiresAuth标记受保护路由router.beforeEach检查 token 是否存在,不存在跳转登录
-
Token 过期处理
- 后端返回 401 → 响应拦截器提示重新登录
- 清理 localStorage,跳转登录页
-
无感刷新
- JWT 有 exp 字段
- Axios 请求拦截器解码 payload → 判断 token 是否快过期
- 快过期 → 调用 refresh 接口获取新 token → 更新 localStorage → 继续请求
-
完整流程(前端 perspective)
登录 → 保存 token → 路由守卫 + Axios 自动带 token ↓ token 快过期 → 自动刷新 token → localStorage 更新 ↓ 请求受保护接口 → 后端验签 → 返回数据
五、最佳实践清单
| 类别 | 建议做法 |
|---|---|
| secret 管理 | 生产环境用环境变量,不硬编码 |
| token 存储 | localStorage 持久化,sessionStorage 可选 |
| Axios 拦截器 | 请求:自动带 token;响应:处理 401 |
| 中间件 | Egg.js:用 ctx.app 获取 app,避免工厂函数 app undefined |
| 路由守卫 | meta.requiresAuth + beforeEach 检查 token |
| token 刷新 | 使用 refreshToken,无感刷新用户体验 |
| 安全性 | refreshToken 可放 httpOnly cookie,避免 XSS |