本身动手改造开源库

前段时间,本身撸了个web项目,后端用的是koa2,并使用了koa-jwt进行鉴权,在使用的时候遇到了些小问题。前端

1、问题

用koa-jwt鉴权的时候,能够经过设置unless路径,使得某些api不用通过鉴权,好比登陆接口:git

app.use(jwtKoa({secret}).unless({
    path: [/^\/api\/login/]
}));
复制代码

固然还有些好比获取文章内容接口,我须要在未登陆时显示文章,修改和删除是须要鉴权,但因为使用了restful api设计,获取文章和删除文章的api路径是同样的,使用前面的这种方式没法区分get与delete请求,致使鉴权没有任何意义了。github

看了下koa-jwt文档上,并无我须要的,只能看下代码咯,毕竟,比文档更全面的就只有代码了。web

2、处理问题

查看koa-jwt代码,发现unless匹配与验证这块是使用的koa-unless,在package.json中也能够看到: json

png
既然这样,咱们就去查看koa-unless的文档,只能找到单一的url匹配和method匹配,并无二者结合的判断方式。没办法,只能去看koa-unless的源码了。

koa-unless

koa-unless目录比较简单,源码就一个index.js,细看代码,后端

if (matchesCustom(ctx, opts) || matchesPath(requestedUrl, opts) ||
    matchesExtension(requestedUrl, opts) ||matchesMethod(ctx.method, opts)) {
    return next();
}
复制代码

焦点到这个if判断上,此处就是在处理http请求的数据与unless函数是否匹配的地方,当可以匹配上时,则能够继续执行后面的函数。api

function matchesPath(requestedUrl, opts) {
    // ...
}
复制代码

matchesPath,该方法用于判断当前请求的url是否符合unless中的路径。数组

function matchesMethod(method, opts) {
    // ...
}
复制代码

该方法用于判断当前请求方式是否在unless的容许范围内。缓存

咱们发现,这几个match匹配函数之间都是或运算符,所以只要任何一者知足,if就能够经过,因此,url与method之间并无关系,所以,并无我想要的功能。bash

小改如下

既然用||没有关联,改为&&是否是就能够了呢?

依旧不行,由于解析规则并无改变,我无法给每一个url增长独立的method。因此得大改一下了。

3、修改组件

我想要的功能是这样的,

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

path中能够像原先同样直接些路径的正则或者字符串,若是这么写了,他的method就由外侧与path同级的method控制(不写这个method,默认全部方法)。固然也能够在path中设置对象的方式规定url和method,这种方式中的method优先级更高。

考虑到路由多的状况下,在初始化的时候,将unless中的url与method解析到一个空对象之中,并以method为key值,而容许的路由则放在key对应的value数组中。这样,当请求的时候就不用整个unless遍历了。

// 请求方式
var methods = ['GET', 'POST', 'PUT', 'DELETE', 'OPTIONS', 'HEAD', 'TRACE', 'CONNECT'];
// 存储 method: [url1, url2 ...]
var map = {};
复制代码

在初始化的时候,解析unless配置:

/**
 * 将用户写的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) => {
            // 当方法后的value为空数组时,表示全部url均会符合
            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);
    });
}
复制代码

既然想要url和method可以相互关联,那么彼此之间确定要有制约,那么将这二者的判断放到同个方法中。删掉if判断中的matchesPath()与matchesMethod()方法,增长matchesPathAndMethod()方法,参数是当前请求的url和请求方式method。

由于前面已经用map缓存了键值关系,因此后面的处理就会简单多了。

/**
 * 处理当前请求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 (typeofp==='string'&&p===path) ||
            (p instanceof RegExp && !!p.exec(path));
    });
}
复制代码

对应method中没有url,则直接false,当对应method下是空数组,则是全部url都ok。其余状况下,则须要依次遍历method下的url是否匹配。

文件附件:koa-unless.js

原文连接:www.zhuyuntao.cn/2019/03/24/…

欢迎关注微信公众号[ 我不会前端 ]或扫描下方二维码!

png
相关文章
相关标签/搜索