Node框架之Egg的中间件 —— 登录鉴权

3,007 阅读3分钟

一. 简介

管理系统可不是谁都能对里面的数据进行操作,只能有权限的管理员才能进行相应的操作。

以下文章都是在 文章一 的基础上编写,所以一些基础的配置,就不再赘述,直接上手写重要代码。接下来就会通过最近自己在写的博客的后台管理系统(下文简称:管理系统),来对 中间件 + 登录 这个功能进行记载;本案例采用的是Restful API的方式给前端返回数据。

本实例用到的插件:mysql2 + egg-sequelize + egg-cors + egg-jwt + md5

二. 编码

1. Egg 后端

1.1. 登录鉴权方案

目前登录鉴权方案有很多,介绍可参考 常见登录鉴权方案 ,以下列出几种:

  • HTTP Auth Authentication

  • Cookie + Session

  • JWT

  • OAuth 本示例中则使用的是JWT的方式,所用到的插件为 egg-jwt

  • 安装:npm install egg-jwt --save

  • 引入egg-jwt包:

    // config/plugin.js
    
    'use strict';
    module.exports = {
       ...
        jwt: {
            enable: true,
            package: 'egg-jwt'
        }
    };
    
  • 配置:

    // config/config.default.js
    
    'use strict';
    module.exports = appInfo => {
      const config = exports = {};
      ...
      config.cluster = {
        listen: {
          port: 1598,
        },
      };
      config.jwt = {
        secret: 'ea86dg645sdfg6sf', // 可自行定义
      };
      return { ...config };
    };
    
  • 使用

    // app/controller/auth.js
    
    'use strict';
    const md5 = require('md5')
    const Controller = require('./base');
    
    class AuthController extends Controller {
       async login() {
           const { ctx, service } = this
           const { username, password } = ctx.request.body
           const md5pwd = md5(password)
           const where = { username, password: md5pwd };
           const result = await service.auth.login({ where })
           this.success(result)
       }
       async getUserInfo() {
           const { ctx, service } = this
           const { token } = ctx.request.query
           const userinfo = await service.auth.getUserInfo(token)
           this.success(userinfo)
       }
    }
    
    module.exports = AuthController;
    
    // app/service/auth.js
    
    'use strict';
    const Service = require('./base');
    
    class AuthService extends Service {
      async login(option) {
        const { ctx, app } = this;
        const { count, rows } = await this._findAll('Users', option);
        const userInfo = rows[0];
        if (count === 1) {
          return app.jwt.sign({
            id: userInfo.id,
            username: userInfo.username,
            password: userInfo.password,
          }, app.config.jwt.secret, { expiresIn: '12h' });
        }
        ctx.throw(403, '账号或密码错误');
      }
      async getUserInfo(token) {
        const { ctx } = this;
        // 解密token
        const decoded = ctx.app.jwt.verify(token, ctx.app.config.jwt.secret);
        return await this._findById('Users', decoded.id);
      }
    }
    
    module.exports = AuthService;
    

1.2. 中间件的使用

官网 可得知,Egg 中间件 有三种使用方式:在应用中使用中间件在框架和插件中使用中间件router 中使用中间件,在不同场景下可选择不同的方式,在本案例中选择了 router 中使用中间件

  • 写法
    在管理系统中,除登录以外的接口,都需要有权限才能请求,所以在 middleware 文件夹中定义中间件文件。如 jwt.js,并实现自定义的功能。

    // middleware/jwt.js
    
    // 在 “router 中使用中间件” 中用不到
    const whiteList = ['/login']   
    
    module.exports = (options) => {
    return async function (ctx, next) {
        //判断接口路径是否在白名单(在 “router 中使用中间件”中不需要验证这一步)
        const isInWhiteList = whiteList.some(item => item == ctx.request.url) 
        if (!isInWhiteList) {
                // 拿到前端传过来的 token
                const token = ctx.request.header.authorization
                if (token) {
                    //解密token
                    const secret = ctx.app.config.jwt.secret
                    const decoded = ctx.app.jwt.verify(token, secret) || 'false'
                    if (decoded !== 'false') {
                        await next()
                    } else {
                        ctx.throw(403, '无效Token')
                    }
                } else {
                    ctx.throw(403, '无Token')
                }
            } else {
                await next()
            }
        }
    }
    
  • 使用

    // router.js
    
    'use strict';
    module.exports = app => {
      const { router, controller, middleware } = app;
      
      // 引入
      const jwt = middleware.jwt(app.config.jwt);
    
      // 用户登录、获取用户信息
      router.post('/admin/login', controller.auth.login);
      router.get('/admin/getUserInfo', jwt, controller.auth.getUserInfo);
    
      // 文章管理
      router.get('/admin/article',jwt, controller.article.index);
      router.get('/admin/article/findById',jwt, controller.article.findById);
      router.post('/admin/article/doAdd',jwt, controller.article.doAdd);
      router.put('/admin/article/doEdit',jwt, controller.article.doEdit);
      router.delete('/admin/article/doDel',jwt, controller.article.doDel);
    };
    

2. Vue 前端

本文主要是讲述Egg中间件的使用。这里只简单说一下前端怎么做:

  • 请求登录接口,将登陆成功后返回的token信息,存入到本地缓存中;
  • 在需要鉴权的接口请求时,将token放入请求头中一并返回给后端做鉴权;
  • 处理后端返回的信息(成功或者失败)。

三. 总结(router 中使用中间件)

第一步: 自定义中间件。在 middleware 文件夹中定义中间件文件,如 jwt.js;
第二步: 使用中间件。在 router.js 中,先引入,再调用;
第三步: 前端使用。

四. 获取源码

点击获取:获取源码


fxwb.png