Alan

此刻想举重若轻,之前必要负重前行

react博客系列文章

何为jwt

egg-jwt

安装egg-jwt

yarn add egg-jwt

配置egg-jwt

  1. 配置config/plugin.js文件
exports.jwt = {
  enable: true,
  package: 'egg-jwt',
};
  1. 配置config/config.default.js文件
// 自己设定的密钥,用于对信息进行签名
config.jwt = {
  secret: 'xxxxxx',
};

实例

后台实现登录操作返回token

  1. 路由层

在第二个参数上加上jwt即可实现对该路由的鉴权

下面的jwt是自定义的中间件auth.js,该文件在下一部分介绍

'use strict';

/**
 * @param {Egg.Application} app - egg application
 */
module.exports = app => {
  const { router, controller, middleware, config } = app;
  const { admin } = controller.admin;
  const jwt = middleware.auth(config.jwt);
  router.post('/admin/login', admin.login);
  // 需要鉴权的路由再第二个参数上加上jwt
  router.get('/admin/get_type_list', jwt, admin.getTypeList);
};
  1. controller层

使用jwt.sign(加密数据, 密钥, [options, callback]) 来生成token

相关配置可以查看jsonwebtoken

async login() {
    const { app, ctx } = this;
    const { username, password } = ctx.request.body;
    const checkValidate = await ctx.service.admin.admin.checkUserValidate(username, password);
    if (checkValidate) {
        // 将信息使用jwt进行签名加密生成token,expiresIn(token有效时间)
        const token = app.jwt.sign({ username, password }, app.config.jwt.secret, { expiresIn: '2h' });
        ctx.body = {
            code: '0001',
            msg: '登录成功',
            token,
        };
    } else {
        ctx.body = {
            code: '0002',
            msg: '用户名或者密码错误,请重试!',
        };
    }
}
  1. service层
async checkUserValidate(username, password) {
  const isValidate = await this.app.mysql.get('admin', { username, password });
  return !!isValidate;
}
  1. 使用postman进行测试

image-20200502125430201

将生成的token返回给前台后使用localStorage.setItemtoken保存到本地

前台传递token进行鉴权

封装axios

新建axios文件对axio进行封装,通过axios的拦截器来实现每次请求时自动在headers上携带token数据到后台,后台使用jwt.verify来检验token的正确性

axios.js

import axios from 'axios';

axios.interceptors.request.use(config => {
  // 登录成功后保存在本地的token
  const token = localStorage.getItem('token');
  config.headers.Authorization = `Bearer ${token}`;
  return config;
});

axios.interceptors.response.use(response => {
  // code是后台接口设置的
  const { code } = response.data;
  if (code === '0003' || code === '0004') {
    window.location.href = '/login';
  }
  return response;
});

export default axios;

封装好后,之后都使用该封装好后的axios进行请求。

后台校验处理

auth.js

'use strict';

module.exports = options => {
  return async function auth(ctx, next) {
    // 获取前台通过axios封装后穿过来的token
    const token = ctx.header.authorization;
    if (token) {
      try {
        // 验证并对token进行解码
        const decode = ctx.app.jwt.verify(token.split(' ')[1], options.secret);
        console.log(decode);
        await next();
      } catch (error) {
        console.log(error.name);
        if (error.name === 'JsonWebTokenError' || error.name === 'TokenExpiredError') {
          // 这里的错误有许多种情况:1.token错误,2.token过期... 这里统一处理为鉴权失败
          ctx.body = {
            code: '0003',
            msg: '用户鉴权失败,请重新登录',
          };
        } else {
          throw error;
        }
      }
    } else {
      ctx.body = {
        code: '0004',
        msg: '您没有登录,请先登录',
      };
    }
  };
};

后台通过/middleware/auth.js中间件来实现给需要鉴权的接口进行鉴权,通过对前台传过来的token数据进行验证jwt.verify来识别登录状态。对JsonWebTokenError错误统一处理,应为auth.js作为中间件,所以接口中出现的其他错误也会在这里被catch到,对于不是JsonWebTokenError的错误,直接把错误抛出去。

评论