jwt学习

31 阅读2分钟

📝 JWT 学习与实战小结

一、概念篇

  1. JWT 是什么

    • JSON Web Token(JWT)是一种自包含的 token,用于客户端与服务端之间安全传递信息。

    • JWT 本质是一个字符串,包含三部分:

      1. Header(头部):声明算法等信息
      2. Payload(载荷):存储用户信息等数据
      3. Signature(签名):用 secret 和 Header+Payload 生成,保证内容不被篡改
  2. Session vs Token

    特性SessionJWT/Token
    存储位置服务器客户端(localStorage/cookie)
    扩展性单机/分布式需共享 session分布式无需共享状态
    验证方式服务器查 session服务器验签 JWT
    安全性服务器掌控依赖 secret + 签名算法
  3. Token 使用原理

    • 用户登录 → 后端生成 JWT → 返回给前端
    • 前端保存 token(localStorage/sessionStorage)
    • 每次请求受保护接口 → 带上 token → 后端验签

二、后端篇(Egg.js)

  1. 安装插件

    npm install egg-jwt
    
  2. 配置 secret

    // config/config.default.js
    exports.jwt = { secret: 'my_jwt_secret_key_123456' };
    
  3. 中间件

    // 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 无效或已过期' };
        }
      };
    };
    
  4. 路由注册

    const jwtAuth = app.middleware.jwtAuth();
    router.post('/login', controller.auth.login);
    router.get('/user/info', jwtAuth, controller.user.info);
    
  5. Controller 生成 JWT

    const token = app.jwt.sign({ username }, app.config.jwt.secret, { expiresIn: '1h' });
    

三、前端基础篇(Vue 3)

  1. 登录表单

    • 用户输入用户名/密码 → 调用 /login → 拿 token
    • 保存 token:localStorage.setItem('jwt_token', token)
  2. 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);
      }
    );
    
  3. 为什么不用 Vue 响应式变量保存 token

    • 页面刷新后内存清空 → token 消失
    • localStorage 持久化存储,刷新页面仍可读取

四、前端进阶篇

  1. 路由守卫

    • meta.requiresAuth 标记受保护路由
    • router.beforeEach 检查 token 是否存在,不存在跳转登录
  2. Token 过期处理

    • 后端返回 401 → 响应拦截器提示重新登录
    • 清理 localStorage,跳转登录页
  3. 无感刷新

    • JWT 有 exp 字段
    • Axios 请求拦截器解码 payload → 判断 token 是否快过期
    • 快过期 → 调用 refresh 接口获取新 token → 更新 localStorage → 继续请求
  4. 完整流程(前端 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