首页 > 其他分享 >Egg.js配置登录验证中间件jwt

Egg.js配置登录验证中间件jwt

时间:2024-09-05 10:25:39浏览次数:11  
标签:const 中间件 await jwt js item ctx res return

Egg.js配置登录验证中间件

一、 安装jwt

npm install egg-jwt

二、 安装完成后在config --》plugin.js文件中开启jwt插件

"use strict";
module.exports = {
  jwt: {
    enable: true,
    package: "egg-jwt",
  },
}

三、 在app文件夹下创建middleware文件夹,创建verifyToken.js(命名随意)

module.exports = (options, app) => {
  return async function jwtMiddleware(ctx, next) {
    // 获取请求头中的 Authorization 字段,通常包含 JWT Token
    const token = ctx.request.header.authorization;
    // 不用token的列表
    const whiteList = [
      '/login',
      '/prod-api/login',
    ];
    // 跳过swagger的路径验证
    if (ctx.path.indexOf('swagger') > -1 || ctx.path.indexOf('favicon') > -1) {
      await next();
      return;
    }
    app.logger.info(ctx.path);
    // 如果没有提供 Token,返回 401 错误
    if (!token && whiteList.indexOf(ctx.path) === -1) {
      ctx.status = 200;
      ctx.body = { code: 401, msg: '会话过期,请重新登录' };
      return;
    }

    if (token) {
      try {
        // 验证 JWT Token
        // 将解码后的用户信息挂载到 ctx.state.user 中,以便后续路由使用
        ctx.state.userInfo = app.jwt.verify(
          token.split('Bearer ')[1],
          app.config.jwt.secret
        );
      } catch (err) {
        // Token 验证失败,返回 401 错误
        ctx.status = 200;
        ctx.body = { code: 401, msg: '会话过期,请重新登录' };
        return;
      }
    }

    // 继续执行下一个中间件或路由处理函数
    await next();
  };
};

四、 同时要在config --》config.default.js中添加jwt配置

// 这里添加中间件(内容就是middleware目录下的中间件文件名)
config.middleware = ['verifyToken']

// jwt配置
config.jwt = {
  secret: '项目token密钥随意填写',
  sign: {
    // jwt.sign(***,***,[options,***])方法中,options的默认设置可以在这里配置;
    // 过期时间24小时
    expiresIn: 24 * (60 * 60), // 多少s后过期。actionToken.js中,jwt.sing(plyload,secret,{expiresIn:number})会被合并,调用时设置优先级更高;
  },
};

配置已经添加好了接下来就要写接口了。

五、 首先在router.js中添加登录路由

import { Application } from 'egg'

export default (app) => {
  const { controller, router } = app
  // 统一前缀方法可根据需求添加登录
  router.prefix('/api')
  // 添加登录控制器
  router.post('/login', controller.sysBasic.login)
}

六、 创建login控制器

1. 需要在app --》controller文件夹下新建sysBasic.js 控制器文件(文件命名需同路由配置一样)
const Controller = require('egg').Controller;

class SysBasicController extends Controller {
  // 登录
  async login(ctx) {
    ctx.body = await ctx.service.system.sysBasic.login(ctx.request.body);
    ctx.status = 200;
  }
  // 获取验证码
  async captchaImage(ctx) {
    ctx.body = await ctx.service.system.sysBasic.captchaImage(ctx.request.body);
    ctx.status = 200;
  }
  // 获取用户信息
  async getInfo(ctx) {
    ctx.body = await ctx.service.system.sysBasic.getInfo();
    ctx.status = 200;
  }

  // 获取路由信息
  async getRouters(ctx) {
    ctx.body = await ctx.service.system.sysBasic.getRoute();
    ctx.status = 200;
  }

  // 更新用户密码
  async updatePwd(ctx) {
    await ctx.service.system.sysBasic.updatePwd(ctx.query);
    ctx.status = 200;
  }
}
module.exports = SysBasicController;
控制器写好后我们需要添加对应的服务(Service)
/*
 * @FilePath: /app/service/system/sysBasic.js
 * @Description:
 * 基础服务,如获取验证码等
 */
const Service = require('egg').Service;
// 验证码插件
const captchapng = require('captchapng');
// 加密模块
const utility = require('utility');
const { v4: uuidv4 } = require('uuid');
// 数据库模块
const { Op } = require('sequelize');
class SysBasicService extends Service {
  constructor(ctx) {
    super(ctx);
    this.session = ctx.session;
    this.getUserData = ctx.getUserData;
    this.ResponseCode = ctx.response.ResponseCode;
    this.ServerResponse = ctx.response.ServerResponse;
    this.objUnderlineToHump = ctx.objUnderlineToHump;
    this.objHumpToUnderline = ctx.objHumpToUnderline;
    this.capitalizeFirstLetter = ctx.capitalizeFirstLetter;
    this.changeToTree = ctx.changeToTree;
    this.toInt = ctx.toInt;
    this.SysUser = ctx.model.SysUser;
    this.SysDept = ctx.model.SysDept;
    this.SysRole = ctx.model.SysRole;
    this.SysMenu = ctx.model.SysMenu;
    this.SysUserRole = ctx.model.SysUserRole;
    this.SysRoleMenu = ctx.model.SysRoleMenu;
  }
  /**
   * 创建验证码
   * @return
   */
  async captchaImage() {
    // 第三方插件,
    const captchaVal = parseInt(Math.random() * 9000 + 1000);
    const p = new captchapng(80, 30, captchaVal); // width,height,numeric captcha
    p.color(205, 205, 205, 128); // First color: background (red, green, blue, alpha)
    p.color(2, 5, 230, 255); // Second color: paint (red, green, blue, alpha)
    const img = p.getBase64();
    // 验证码存session
    this.session.code = {
      value: utility.md5(utility.md5(captchaVal + '')),
      time: Date.now(),
    };
    return {
      // TODO这里可以设置开启或者关闭系统的验证码,需要存在数据库中,在系统缓存中实现
      captchaEnabled: true,
      img,
      msg: '操作成功',
      uuid: uuidv4(),
      code: 200,
    };
  }

  /**
   * 登录
   * @param params
   * @return
   */
  async login(params) {
    console.log('加密后的密码', utility.md5(params.password));
    if (utility.md5(utility.md5(params.code)) !== this.session.code.value) {
      return {
        code: 600,
        msg: '验证码不正确,请输入正确验证码',
      };
    }
    const res = await this.SysUser.findOne({
      where: {
        user_name: params.username,
        password: utility.md5(params.password),
      },
      raw: true,
    });
    if (!res) {
      return this.ServerResponse.createByErrorCodeMsgBusiness(50001);
    }
    const token = this.app.jwt.sign(
      {
        userId: res.user_id,
        userName: params.username,
      },
      this.app.config.jwt.secret
    );
    this.ctx.handleUser = {
      userId: res.user_id,
      userName: params.username,
    };
    return {
      code: 200,
      msg: '操作成功',
      token,
    };

  }

  /**
   * 更新密码
   * @param {*} params
   */
  async updatePwd(params) {
    const userData = await this.getUserData();
    console.log(userData);
    const res = await this.SysUser.findOne({
      where: {
        user_name: userData.userName,
        password: utility.md5(params.oldPassword),
      },
      raw: true,
    });
    if (res) {
      const findRepeat = await this.SysUser.findByPk(userData.userId);
      await findRepeat.update({
        user_id: userData.userId,
        password: utility.md5(params.newPassword),
      });
      return this.ServerResponse.createBySuccessMsg('修改成功');
    }
    throw '原始密码不正确';

  }

  /**
   * 获取登录信息
   */
  async getInfo() {
    const userResuserData = await this.getUserData();
    const { userId } = userResuserData;
    const userRes = await this.SysUser.findAll({
      where: {
        user_id: userId,
      },
      include: [{ model: this.SysDept }, { model: this.SysRole }],
    });
    const permissions = [];
    const roles = [];
    // 判断是否为超管权限
    if (
      userRes[0].sys_roles[0] &&
      userRes[0].sys_roles[0].role_key === 'admin'
    ) {
      permissions.push('*:*:*');
    } else {
      userRes[0].sys_roles.forEach(element => {
        permissions.push(element.role_key);
      });
    }
    userRes[0].sys_roles.forEach(element => {
      roles.push(element.role_key);
    });
    // 返回成若依的数据结构
    userRes[0].dataValues.roles = this.objUnderlineToHump(
      JSON.parse(JSON.stringify(userRes[0].sys_roles))
    );
    userRes[0].dataValues.dept = this.objUnderlineToHump(
      JSON.parse(JSON.stringify(userRes[0].sys_dept))
    );
    delete userRes[0].dataValues.sys_roles;
    delete userRes[0].dataValues.sys_dept;
    return {
      code: 200,
      msg: '操作成功',
      permissions,
      user: this.objUnderlineToHump(userRes[0].dataValues),
      roles,
    };
  }

  /**
   * 获取用户的路由权限
   * 路由返回参数及格式
   *
   * @param {*} params
   */
  async getRoute() {
    const userResuserData = await this.getUserData();
    const { userName, userId } = userResuserData;
    let res;
    if (userName === 'admin') {
      // 超管查询所有的菜单
      res = await this.SysMenu.findAll({
        where: {
          status: '0',
          [Op.or]: [
            {
              menu_type: 'M',
            },
            {
              menu_type: 'C',
            },
          ],
        },
        order: [
          [ 'parent_id', 'asc' ],
          [ 'order_num', 'asc' ],
        ],
      });
    } else {
      // 查询 user对应的roleID
      const rowIds = await this.SysUserRole.findAll({
        where: {
          user_id: userId,
        },
      });
      const rowArr = [];
      rowIds.forEach(element => {
        rowArr.push(element.dataValues.role_id);
      });
      // 查询所有的匹配权限的菜单
      res = await this.SysRoleMenu.findAll({
        where: {
          role_id: {
            [Op.or]: rowArr,
          },
        },
        include: [
          {
            model: this.SysMenu,
          },
        ],
      });
      // console.log(res);
      res = res.map(item => {
        return item.sys_menu;
      });
    }

    res = res.map(item => {
      if (item) {
        item = this.objUnderlineToHump(item.dataValues);
        // 当为根路径并且不为外链时path加上’/‘
        item.name = this.capitalizeFirstLetter(item.path);
        if (item.parentId === 0 && item.isFrame) {
          item.path = '/' + item.path;
        }
        return item;
      }
    });
    res = res.filter(
      item => item && (item.menuType === 'M' || item.menuType === 'C')
    );
    res = this.changeReturnTree(this.changeToTree(res, 'parentId', 'menuId'));
    return this.ServerResponse.createBySuccessMsgAndData('查询成功', res);
  }

  /**
   * 将路由树的数据库数据转换为对应的路由数据
   * @param {*} data
   */
  changeReturnTree(data) {
    data = data.map(item => {
      if (item.children) {
        item.children = this.changeReturnTree(item.children);
      }
      return this.getRouteLint(item);
    });
    return data;
  }
  getRouteLint(item) {
    let component = item.menuType === 'M' ? 'Layout' : item.component;
    if (item.parentId !== 0 && component === 'Layout') {
      component = 'ParentView';
    }
    const returnData = {
      // 路由的名称
      name: item.name,
      // 路径的地址
      path: item.path,
      // 菜单是否隐藏
      hidden: item.visible !== '0',
      // 面包屑是否能点击  暂定菜单层次的才可以点击
      // redirect: item.menuType == "C" ? "Redirect" : "noRedirect",
      // 组件地址
      component,
      // 是否长展示
      // alwaysShow: item.menuType == "M" ? "" : "noRedirect",
      // 其他数据
      meta: {
        // 菜单名称
        title: item.menuName,
        // 图标
        icon: item.icon,
        // 是否缓存
        noCache: !item.isCache,
        // 跳转路径
        link: !item.isFrame ? item.path : null,
      },
    };
    if (item.children) {
      returnData.children = item.children;
    }
    return returnData;
  }
}

module.exports = SysBasicService;

还有关联的相对应的model文件这里可以自行创建了或者使用
egg-sequelize-auto 插件进行自动生成
到这里就可以使用jwt 验证前端传过来的authorization(token)字段。

标签:const,中间件,await,jwt,js,item,ctx,res,return
From: https://blog.csdn.net/weixin_43381610/article/details/141896171

相关文章

  • js中的addEventListener
    addEventListener()方法:addEventListener() 方法为指定元素指定事件处理程序。addEventListener() 方法为元素附加事件处理程序而不会覆盖已有的事件处理程序。您能够向一个元素添加多个事件处理程序。您能够向一个元素添加多个相同类型的事件处理程序,例如两个......
  • js 识别身份证号的性别、生日、年龄
    //通过身份证号计算年龄、性别、出生日期,userCard:身份证号的字符串,不能是数字functionidCard(userCard){//身份证正则表达式varre=/^\d{6}(((19|20)\d{2}(0[1-9]|1[0-2])(0[1-9]|[1-2][0-9]|3[0-1])\d{3}([0-9]|x|X))|(\d{2}(0[1-9]|1......
  • 240java jsp SSM Springboot小区物业管理系统报修小区环境缴费管理(源码+文档+开题+运
    项目技术:Springboot+Maven+Vue等等组成,B/S模式+Maven管理等等。环境需要1.运行环境:最好是javajdk1.8,我们在这个平台上运行的。其他版本理论上也可以。2.IDE环境:IDEA,Eclipse,Myeclipse都可以。推荐IDEA;3.tomcat环境:Tomcat7.x,8.x,9.x版本均可4.硬件环境:windows......
  • 239java jsp SSM Springboot超市便利店信息管理系统超市供应商信息商品采购收银员工管
    项目技术:Springboot+Maven+Vue等等组成,B/S模式+Maven管理等等。环境需要1.运行环境:最好是javajdk1.8,我们在这个平台上运行的。其他版本理论上也可以。2.IDE环境:IDEA,Eclipse,Myeclipse都可以。推荐IDEA;3.tomcat环境:Tomcat7.x,8.x,9.x版本均可4.硬件环境:windows......
  • 前端常用的echart获取地图json方法
    一、世界地图,不细分国家相关链接:geojson在线绘制 Json文件链接:world-min.json二、世界地图,包含各个国家 Json文件链接:world.json三、中国地图,省市区县(阿里DataV.GeoAtlas)相关链接:阿里DataV.GeoAtlas在线绘制  Json文件链接:china-min.json、china.json修复右手定......
  • MySQL JSON 数据类型
    JSON数据类型是MySQL5.7.8开始支持的。在此之前,只能通过字符类型(CHAR,VARCHAR或TEXT)来保存JSON文档。相对字符类型,原生的JSON类型具有以下优势:在插入时能自动校验文档是否满足JSON格式的要求。优化了存储格式。无需读取整个文档就能快速访问某个元素的值。在JS......
  • JS 对象深浅拷贝
    1.浅拷贝的原理和实现自己创建一个新的对象,来接受你要重新复制或引用的对象值。如果对象属性是基本的数据类型,复制的就是基本类型的值给新对象;但如果属性是引用数据类型,复制的就是内存中的地址,如果其中一个对象改变了这个内存中的地址,肯定会影响到另一个对象方法一:obje......
  • 【D3.js in Action 3 精译_021】第三章 数据的处理 + 3.1 理解数据
    当前内容所在位置第一部分D3.js基础知识第一章D3.js简介(已完结)1.1何为D3.js?1.2D3生态系统——入门须知1.3数据可视化最佳实践(上)1.3数据可视化最佳实践(下)1.4本章小结第二章DOM的操作方法(已完结)2.1第一个D3可视化图表2.2环境准备2.3......
  • vscode launch.json 模板备忘
    {//UseIntelliSensetolearnaboutpossibleattributes.//Hovertoviewdescriptionsofexistingattributes.//Formoreinformation,visit:https://go.microsoft.com/fwlink/?linkid=830387"version":"0.2.0",&quo......
  • jspWeddingSystem18q852
    jspWeddingSystem18q852本系统(程序+源码+数据库+调试部署+开发环境)带论文文档1万字以上,文末可获取,系统界面在最后面。系统程序文件列表项目功能用户,摄影师,商品信息,预定信息,订单支付,摄影预约,摄影费用技术要求:   开发语言:JSP前端使用:HTML5,CSS,JSP动态网页技术......