使用egg-jwt进行路由鉴权以及登陆、注册接口的实现

前面我们已经链接上了数据库, 并且实现了基本的读写操作, 接下来我们来实现需要的几个接口

注册api

根据上面的流程, 我们来实现基本的注册能力

Controller

首先我们来新建一个控制器user, 用来处理用户相关的操作

在控制器内我们需要做几件事

  1. 获取到路由提交的用户名和密码
    1. 注册我们一般适用post提交, 所以此处我们使用query抓取参数
  1. 用获取的用户名和密码调用数据库查询
    1. 数据库相关的操作我们放在service下,我们此处可以先进行调用
  1. 返回信息
    1. 先将查询后的result直接返回
ini 复制代码
const { Controller } = require('egg');

class UserController extends Controller {
  async register() {
    const { ctx } = this;
    const { username, password } = ctx.query;
    const body = {
      code: 200,
      msg: '注册成功',
    };
    const result = await ctx.service.user.getUserByName(username);
    if (!result) {
      const res = await ctx.service.user.register({ username, password });
      if (res.affectedRows === 1) {
        body.code = 200;
        body.msg = '注册成功';
      } else {
        body.code = 500;
        body.msg = '注册失败';
      }
    } else {
      // 用户名已注册
      body.code = 500;
      body.msg = '用户名已注册';
    }
    ctx.body = body;
  }
}

module.exports = UserController;

代码里面,我们先调用getUserByName查询用户名是否呢已经注册, 根据结果我们才进行数据的添加注册

Service

根据上面控制器以及注册流程, 我们在seivice里面需要提供两个方法,分别处理

  1. 根据username字段查询用户名是否已经注册
  2. 注册功能, 也就是数据插入

创建app/service/user.js进行数据库相关的操作

javascript 复制代码
const { Service } = require('egg');

class UserService extends Service {
  // 根据username字段查询用户信息
  async getUserByName(username) {
    const { app } = this;
    const result = await app.mysql.get('user', { username });
    return result;
  }
  // 注册
  async register(params) {
    const { app } = this;
    const { username, password } = params;
    const ctime = new Date();
    const result = await app.mysql.insert('user', { username, password, ctime });
    return result;
  }
}

module.exports = UserService;

路由

最后我们来创建一个注册的路由

arduino 复制代码
module.exports = app => {
  const { router, controller } = app;
  // 注册
  router.post('/register', controller.user.register);
};

验证

完成上面所有的操作, 我们打开postman 进行一下验证, 请求两次, 分别验证注册一级重复注册

注册:

重复注册:

登陆

egg-jwt

jwt是什么此处就不多说了,百度上概念已经很详细了,我们使用的 egg-jwt 来实现路由鉴权部分

安装egga-jwt

安装依赖

sql 复制代码
npm add egg-jwt --save

注册插件

java 复制代码
module.exports = {
  ...
    jwt: {
  enable: true,
    package: 'egg-jwt',
    },
    ...

}

配置

arduino 复制代码
config.jwt = {
  secret: '123456', // 自定义的密钥字段
};

登陆api

首先我们在app/controller/user.js 中定一个login controller , 来实现登陆相关的逻辑

ini 复制代码
class UserController extends Controller {
  async login() {
    const { ctx, app } = this;
    const { username, password } = ctx.query;
    const body = {
      code: 200,
      msg: '登陆成功',
      data: {},
    };
    const userinfo = await ctx.service.user.getUserByName(username);
    if (userinfo && userinfo.password === password) {
      // 200
      body.code = 200;
      body.msg = '登陆成功';
    } else if (userinfo && userinfo.password !== password) {
      // password error
      body.code = 401;
      body.msg = '密码错误';
    } else {
      // 用户不存在
      body.code = 401;
      body.msg = '用户不存在';
    }
    ctx.body = body;
  }
}

通过ctx.query抓取到用户提交的信息 username `` password , 然后根据 username 去数据库 user表中查询相关用户信息, 进行对比。

以下我们定义 getUserByName 方法, 用来根据 username 去数据库 user表中查询相关用户信息

scala 复制代码
class UserService extends Service {

  async getUserByName(username) {
    const { app } = this;
    const result = await app.mysql.get('user', { username });
    return result;
  }
}

此处我们已经实现了基本的login 接口, 但登陆成功后, 我们需要返回token, 提供给前台, 用来进行后续接口请求的鉴权。

在上面的login 方法中继续补充token 返回

css 复制代码
body.data.token = app.jwt.sign({
  ...userinfo,
  time: Date.now(),
}, app.config.jwt.secret);

我们用当前时间生成token, 方便我们后面对比。此处需要说明我们在config中声明的变量, 在运行时都会挂载到app中的config字段上。

鉴权中间件

接下来我们需要思考一个问题, token生成后如何实现接口鉴权?

思路其实都是一样的, 就是在请求的接口中抓取token字段, 进行解析, 然后对比是否有效, 但具体如何实现才能更高效、简单呢?

前面我们在实现api的时候在每一个 Controller也就是接口的实现内都可以访问到ctx, 此时我们进行token 的判断, 也可以实现接口的鉴权, 但问题是, 这样写在每一个接口的实现内, 效率太低, 同时不方便维护。

此时, 我们就需要用到middleware 中间件, 在egg中我们需要开发中间件的时候只需要在 app/middleware/下直接新建就可以自动挂载到app上

接下来我们实现路有鉴权的中间件

ini 复制代码
module.exports = () => {
  return async function jwterror(ctx, next) {
    const { app, header: { token } } = ctx;
    if (!token) {
      ctx.body = {
        msg: '缺少token',
        code: 401,
      };
      return;
    }
    try {
      const userinfo = app.jwt.verify(token, app.config.jwt.secret);
      console.log(userinfo);
      await next();
    } catch (error) {
      ctx.body = {
        msg: 'token已过期,请重新登录',
        code: 401,
      };
    }

  };
};

抓取到 header 中的token 字段后, 我们调用 jwt.verify() 来进行解析。获取到 userinfo 之后我们只需要对比一下是否生效就可以了, 此处对比根据需求自行实现。

中间件实现后, 具体如何调用, 常见的会有两种方式

  1. 全局

也就是挂载到全局, 会在所有的路由上生效。

具体就是 在config/config.default.js中定义

ini 复制代码
config.middleware = [ 'jwterror' ];
  1. 按需

但有时候我们又不是所有的路由都需要鉴权, 就比如登陆、注册。 前面说了middleware会挂载到app上, 所以我们在定义路有的时候在需要的路由上进行调用就可以了

ini 复制代码
module.exports = app => {
  const { router, controller, middleware } = app;
  const jwterror = middleware.jwterror();

  
  router.get('/list', jwterror, controller.account.list);
};

需要注意的是,middleware挂载的是定义的jwterror方法, 执行后return 出来的是内部的jwterror 函数。

相关推荐
颜漠笑年1 分钟前
可迭代对象≠数组,一起来揭开for...of背后隐藏的秘密吧
前端·javascript
GIS之路2 分钟前
GeoTools 数据模型
前端
拾光拾趣录3 分钟前
Vue中v-if与v-for同元素使用的陷阱
前端·vue.js
拾光拾趣录3 分钟前
浏览器存储:从Cookie到IndexedDB
前端·浏览器·indexeddb
拾光拾趣录3 分钟前
前端静态资源本地缓存:从秒开到省流量
前端·性能优化·浏览器
脑袋大大的1 小时前
判断当前是否为钉钉环境
开发语言·前端·javascript·钉钉·企业应用开发
军军君011 小时前
基于Springboot+UniApp+Ai实现模拟面试小工具二:后端项目搭建
前端·javascript·spring boot·spring·微信小程序·前端框架·集成学习
quweiie2 小时前
tp8.0\jwt接口安全验证
前端·安全·jwt·thinkphp
xiaoyan20152 小时前
最新Flutter3.32+Dart3仿微信App聊天实例
前端·flutter·dart
汪敏wangmin2 小时前
Fiddler-抓包后直接生成Loadrunner脚本或者Jmeter脚本
前端·jmeter·fiddler