前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >koa2使用jwt鉴权

koa2使用jwt鉴权

作者头像
4O4
发布2022-04-25 19:22:40
4320
发布2022-04-25 19:22:40
举报
文章被收录于专栏:404404

安装

koa-jwt基于jsonwebtoken进行封装。

代码语言:javascript
复制
yarn add jsonwebtoken koa-jwt

使用

app.js
代码语言:javascript
复制
const koajwt = require('koa-jwt');

// 错误处理
app.use((ctx, next) => {
  return next().catch((err) => {
    if (err.status === 401) {
      ctx.status = 401;
      ctx.body = 'use authorization to get access\n';
    } else {
      throw err;
    }
  })
})

app.use(koajwt({
  secret: 'key'
}).unless({
  path: [/\/user\/login/]
}));
user.js
代码语言:javascript
复制
require('../db/connection');
const request = require('request-promise');
const jwt = require('jsonwebtoken');
const User = require('../schemas/user');
const appid = require('../config').appid;
const secret = require('../config').secret;
const jwtKey = require('../config').jwtKey;

class UserController {
  async login(ctx, next) {
    const data = ctx.request.body;

    if (!data.username || !data.password) {
      return ctx.body = {
        code: '0',
        data: null,
        msg: 'invalid parameter'
      }
    }

    const result = await User.findOne({
      username: data.username,
      password: data.password
    })

    if (result !== null) {
      const token = jwt.sign({
        _id: result._id
      }, jwtKey, { expiresIn: '2h' });
      return ctx.body = {
        code: '1',
        data: token,
        msg: 'success'
      }
    } else {
      return ctx.body = {
        code: '0',
        data: null,
        msg: 'invalid username or password'
      }
    }
  }
}

module.exports = new UserController();
router
代码语言:javascript
复制
const router = require('koa-router')();
const userctrl = require('../controllers/user');

router.post('/user/login', userctrl.login);

module.exports = router;
未认证
获取token
已认证

扩展

至此已经实现了基础鉴权,但是由于这里使用了restful api,获取资源的操作与修改、删除操作使用的是相同的路径,而koa-unless只支持单一的url匹配或者method匹配,两者无法混用,于是需要改写koa-unless:

index.js
代码语言:javascript
复制
/**
 * Koa unless middleware. Attach to any middleware and configure it to prevent/permit the
 * middleware in question to be executed.
 *
 * @module koa-unless
 */

'use strict';
var url = require('url');
// 请求方式
var methods = ['GET', 'POST', 'PUT', 'DELETE', 'OPTIONS', 'HEAD', 'TRACE', 'CONNECT'];
// 存储 method: [url1, url2 ...]
var map = {};

/** Creates a wrapper middleware that verifies if the original middleware should be skipped. */
module.exports = function (options) {
  var originalMiddleware = this;

  // If a custom function was passed directly, creates a new object literal that holds it as a property called custom.
  var opts = typeof options === 'function' ? { custom: options } : options;
  opts.useOriginalUrl = (typeof opts.useOriginalUrl === 'undefined') ? true : opts.useOriginalUrl;

  filterUnless(map, options);
  // console.log('map', map);

  // Returns the middleware that wraps the original one.
  return function (ctx, next) {
    var requestedUrl = url.parse((opts.useOriginalUrl ? ctx.originalUrl : ctx.url) || '', true);

    // any match means 'skip original middleware'
    if (matchesCustom(ctx, opts) || matchesPathAndMethod(requestedUrl, ctx.method, opts) || 
      matchesExtension(requestedUrl, opts)) {
      return next();
    }

    return originalMiddleware(ctx, next);
  };
};

/**
 * Returns boolean indicating whether the custom function returns true.
 *
 * @param ctx - Koa context
 * @param opts - unless configuration
 * @returns {boolean}
 */
function matchesCustom(ctx, opts) {
  if (opts.custom) {
    return opts.custom(ctx);
  }
  return false;
}

/**
 * Returns boolean indicating whether the requestUrl matches against the paths configured.
 *
 * @param requestedUrl - url requested by user
 * @param opts - unless configuration
 * @returns {boolean}
 */
function matchesPath(requestedUrl, opts) {
  var paths = !opts.path || Array.isArray(opts.path) ? 
    (Object.prototype.toString.call(opts.path[0]) === '[object Object]' ? getParams(opts.path, 'url') : opts.path) : 
    [opts.path];

  if (paths) {
    return paths.some(function (p) {
        return (typeof p === 'string' && p === requestedUrl.pathname) ||
          (p instanceof RegExp && !!p.exec(requestedUrl.pathname));
      });
  }

  return false;
}

/**
 * Returns boolean indicating whether the requestUrl ends with the configured extensions.
 *
 * @param requestedUrl - url requested by user
 * @param opts - unless configuration
 * @returns {boolean}
 */
function matchesExtension(requestedUrl, opts) {
  var exts = !opts.ext || Array.isArray(opts.ext) ?
    opts.ext : [opts.ext];

  if (exts) {
    return exts.some(function(ext) {
      return requestedUrl.pathname.substr(ext.length * -1) === ext;
    });
  }
}

/**
 * Returns boolean indicating whether the request method matches the configured methods.
 *
 * @param method - method used (GET, POST, ...)
 * @param opts - unless configuration
 * @returns {boolean}
 */
function matchesMethod(method, opts) {
  var methods = !opts.method || Array.isArray(opts.method) ?
    (Object.prototype.toString.call(opts.path[0]) === '[object Object]' ? getParams(opts.path, 'method') : opts.method) : 
    [opts.method];

  if (methods) {
    return !!~methods.indexOf(method);
  }
}
/**
 * 处理当前请求url和method是否符合unless中的配置
 * @param {Object} requestedUrl 请求url相关信息
 * @param {String} method       请求方式 
 */
function matchesPathAndMethod(requestedUrl, method) {
  var path = requestedUrl.pathname,
    mets = map[ method.toLowerCase() ];
  if (!mets) {
    // 没这个方法
    return false;
  }

  if (!mets.length) {
    // 长度是0,证明所有请求都可以
    return true;
  }

  return mets.some(function (p) {
    return (typeof p === 'string' && p === path) ||
      (p instanceof RegExp && !!p.exec(path));
  });
}

/**
 * 将用户写的unless配置转到map数据中
 * @param {Object} map 需要存储到的空对象
 * @param {Object} opts 填写的unless配置
 */
function filterUnless(map, opts) {
  // 处理不写外层method时,默认支持所有请求方式
  var mes = opts.method ? opts.method : methods;

  if (Array.isArray(opts.path)) {
    opts.path.forEach((item) => {
      var method = [],
        url = '';
      if (Object.prototype.toString.call(item) === '[object Object]') {
        // path中的是对象的,则查找他的path和method
        url = item.url;
        method = item.method || mes;
      } else {
        // 单个字符串或正则
        url = item;
        method = mes;
      }
      // 记录
      record(map, method, url);
    });
  } else if (Array.isArray(opts.method)) {
    // 没有path,检测下是否有method
    opts.method.forEach((met) => {
      map[ met.toLowerCase() ] = [];
    });
  }
}
// 将 key: ulr1记录到map中
function record(map, method, url) {
  method.forEach((met) => {
    if (!map[ met.toLowerCase() ]) {
      // 无值时,需要先创建空数组
      map[ met.toLowerCase() ] = [];
    }
    map[ met.toLowerCase() ].push(url);
  });
}

将index.js拷贝至node_modules下koa-unless文件夹即可。

使用
代码语言:javascript
复制
app.use(jwtKoa({secret}).unless({
    method: ['POST'],
    path: [/^\/api\/login/,
        {url: /^\/api\/publicKey/, method: ['GET']}]
}));

以上method表示总的支持的请求方式,path下是具体路径,path中路径跟随的method的优先级更高,所以以上login是post请求,publicKey是get请求。若与path同级的method不写,则所有method都支持。具体路径内的method不写,则跟随外层method。

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2020-08-29 ,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 安装
  • 使用
    • app.js
      • user.js
        • router
          • 未认证
            • 获取token
              • 已认证
              • 扩展
                • index.js
                  • 使用
                  领券
                  问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档