Express 项目环境搭建 -- 8. 权限校验中间件

权限校验对一个后端服务来说是一个躲不开的环节,使用中间件的方式实现 Express 的权限校验。

解析.文件

其实有第三方模块解析.env文件的模块,但是还是想自己解析一下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
// utils/doFile
const fs = require("fs");
const { promisify } = require("util");
const ReadFilePromise = promisify(fs.readFile);
const path = require("path");
let config = null;

/**
* @description 获取。env 文件
* @author bubao
* @date 2022-01-24 19:01:13
* @param {boolean} [force=false] 强制刷新
* @return {*} config
*/
async function ReadDotFile(force = false) {
if (config && !force) {
return config;
}
const dotData = await ReadFilePromise(path.join(__dirname, "../.env"));
const item = dotData.toString().split("\n").filter((v, index) => {
return v.length;
});
config = {};
console.log(item);
item.forEach((v, i) => {
const [key, value] = v.split("=").map(v => v.trim().replace(/"|'/g, ""));
config[key] = value;
});
console.log("config", config);
return config;
}

module.exports = ReadDotFile;

token 生成与校验解析

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
// utils/token
const jwt = require("jsonwebtoken");
const Redis = require("../src/db/redis");
const dotFile = require("./dotFile");
const MyError = require("./MyError");

async function generateToken(paylod, expiresIn = 60 * 60 * 2) {
const { JWT_TOKEN } = await dotFile();
return jwt.sign(paylod, JWT_TOKEN, {
expiresIn
});
}
/**
* @description 验证 token
* @author bubao
* @date 2022-01-24 13:01:06
* @param {string} token token
* @param {number} [type=1] 1: accessToken 2:refreshToken
*/
async function verifyToken(token, type = 1) {
let res;
try {
const { JWT_TOKEN } = await dotFile();
res = jwt.verify(token, JWT_TOKEN);
} catch (error) {
// info token 超时或者无效 token
throw new MyError(40003);
}
const redis = Redis.init();
const redisToken = await redis.get(`${res.id}#${type === 1 ? "access_token" : "refresh_token"}`);
if (redisToken !== token) {
// info 用户重新登录,旧 token 失效
throw new MyError(40003);
}
return res;
}

module.exports = { generateToken, verifyToken };

鉴权中间件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
// src/middleware/auth.js
const Path2Regexp = require("path-to-regexp"); // 正则匹配路径
const { errcode, verifyToken } = require("../../utils/index");

function AuthenticationMiddleWare(pathObj = { }) {
const includes = pathObj.includes;
const excludes = pathObj.excludes;
const [pathIncludesMap, pathExcludesMap] = [
{ GET: {}, POST: {}, PUT: {}, DELETE: {}, OPTIONS: {} },
{ GET: {}, POST: {}, PUT: {}, DELETE: {}, OPTIONS: {} }
];
Array.isArray(includes)
? includes.forEach(item => {
const { method, path } = item;
pathIncludesMap[method][Path2Regexp.pathToRegexp(path)] = Path2Regexp.pathToRegexp(path);
})
: excludes &&
excludes.forEach(item => {
const { method, path } = item;
pathExcludesMap[method][Path2Regexp.pathToRegexp(path)] = Path2Regexp.pathToRegexp(path);
})
;
const f = async (req, res, next) => {
// 如果 excludes 为空
let needAuth = true;
if (!pathExcludesMap && !pathIncludesMap) {
// 都为空则对所有的路由检验
needAuth = true;
} else if (pathExcludesMap) {
// 有 excludes
const pathList = pathExcludesMap[req.method];
for (const element in pathList) {
if (pathList[element].test(req.url)) {
needAuth = false;
break;
}
}
} else {
// 有 excludes
needAuth = false;
const pathList = pathIncludesMap[req.method];
for (const element in pathList) {
if (element.test(req.url)) {
needAuth = true;
break;
}
}
}
if (needAuth) {
const Authorization = req.headers.authorization;
if (Authorization) {
const token = Authorization.replace(/^Bearer /, "");
try {
const decodeToken = await verifyToken(token);
req.decodeToken = decodeToken;
next();
} catch (error) {
const { status, body } = errcode(40003);
res.status(status);
res.send(body);
}
} else {
const { status, body } = errcode(40002);
res.status(status);
res.send(body);
}
} else {
next();
}
};
return f;
}

module.exports = AuthenticationMiddleWare;

使用中间件

app.js中添加中间件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
const express = require("express");
const authMiddleware = require("./src/middleware/auth");

app.use(
authMiddleware({
excludes: [{
method: "POST",
path: "/api/v1/auth"
}, {
method: "POST",
path: "/api/v1/register"
}]
})
);

Express 项目环境搭建 -- 8. 权限校验中间件
https://bubao.github.io/posts/fcdc8fbe.html
作者
一念
发布于
2022年3月7日
许可协议