express 登录鉴权以及接口级别的权限控制

express 登录鉴权以及不同级别的权限控制

理解 handler

目前来说我的理解就是,express每次收到http请求都会暂时拦截,我们编写的这些 handler 就是在处理这些请求,handler 通常会有以下参数:

  • req:数据类型为 Request,这里包含了请求的完整信息,包括请求体,请求头部等等,我们可以在这里获取用户带的请求头,比如 token 等等。
  • res:响应对象,这个对象包含了非常多的方法,我们可以在响应之前做一些操作,比如查询数据库、设置响应头等等。
  • next:这是一个方法,这个方法如果调用,则会进入下一个 handler。
  • err:这个参数只会出现在 ErrorRequestHandler 里面,也就是说当我们自定义了错误处理 errorHandler 并且代码运行确实到了这里,那么 express 会把这个错误信息传递出来。

拦截请求

在理解了 express handler 之后,我们就可以自定义一个 handler 放在所有 handler 之前,把这个 handler 作为整个系统的请求拦截器,在这里我们可以通过是否调用 handlernext 方法来决定是否继续进入后面的 handler 里面(思考:这是不是和 vue-router 的路由守卫很类似呢?)。

jsonwebtoken 使用

我们可以使用常见的 token 来做登录鉴权,可以使用 jsonwebtoken这个第三方库。

安装

我们需要安装 jsonwebtoken 这个库以及它对应的 ts 类型声明包 @types/jsonwebtoken

pnpm add jsonwebtoken
pnpm add @types/jsonwebtoken -D

生成 token

import jwt from "jsonwebtoken";
import { BaseObject } from "models/index.model";

export const SECRET_KEY = "hanshufei_secret_key";

// token 有效期
export const TOKEN_INVALID_TIME = 24 * 60 * 60;

/**
 *
 * @param payload token的载荷
 * @returns {string} token
 * @description 生成token
 */
export function generateToken(payload: BaseObject) {
  const token = jwt.sign(payload, SECRET_KEY, {
    expiresIn: TOKEN_INVALID_TIME,
  });
  return token;
}

我们生成 token 后就可以在用户登录后发给用户,然后用户可以每次请求的时候带上 token 用来鉴权就好。

拦截请求实现

按照前面说的,我们可以自己定义 handler 来拦截请求:

export const authHandler: RequestHandler = (req, res, next) => {
  const token = req.headers[HEADER_TOKEN_KEY.toLowerCase()] as string;
  if (isWhiteList(req.url)) {
    next();
    return;
  }
  if (!token) {
    res.status(RespCode.UNAUTHORIZED).send("Unauthorized");
  } else {
    jwt.verify(token, SECRET_KEY, function (err, decoded) {
      console.log({ decoded, err });
      if (err) {
        res
          .status(RespCode.UNAUTHORIZED)
          .send(new BaseResponse(false, err.message, RespCode.UNAUTHORIZED));
      } else {
        next();
      }
    });
  }
};

比如上面的这个 handler, 我们可以使用 jtw.verify 方法来判断用户传来的 token 是否合法,只有携带合法的 token 才会进入下一个 handler,如果 token 不合法,就返回 401 状态并且返回失败的请求。
我们还可以再一进来的时候先判断白名单(毕竟不是所有的路由都需要权限的)。

不同级别的权限控制

通过上面的内容,我们现在可以实现接口的登录拦截,但是如果我们系统中的不同操作需要涉及到不同级别的权限,我们就需要更加精细的去控制每个接口的权限级别,然而如果我们在每个接口的业务逻辑里面去重复的写判断权限的逻辑是非常繁琐且不好统一控制的。因此,同样的,利用 expresshandler,我们可以在每个需要特殊控制权限接口的路由里面增加一个handler,比如下面的这个例子:

userRouter.delete(
  "/:_id",
  PermissionRequire(Authority.ADMIN),
  async (req, res) => {
    const result = await userService.deleteUsers([req.params._id]);
    res.send(new DeleteResponse(result, 1).response);
  }
);

注意:PermissionRequire 方法这里的参数是通过剩余参数(…rest)的方式接受的,因此这里并非传入数组而是依次传入就好

在上面这个接口中,我们可以看出这应该是一个删除用户的操作,在这个系统中,删除用户是一个敏感且需要高级别权限的操作,因此我们通过加入 PermissionRequire这个方法返回的 handler 并传入需要的权限的列表,来控制该接口,我们再这个 handler 里面去统一做权限判断的逻辑,以及统一处理权限不足的错误。该方法和对应返回的handler示例如下:

// PermissionRequire 方法
/**
 *
 * @param {Authority[]} authorities 该接口需要的权限,默认(不传或传undefined)是全部用户
 * @returns {RequestHandler} 返回express的handler
 */
export const PermissionRequire = (
  ...authorities: Authority[]
): RequestHandler => {
  const handler: RequestHandler = (req, _res, next) =>
    permissionHandler(req, _res, next, authorities);
  return handler;
};

下面是 handler 的具体实现:

/**
 *
 * @param {Authority[]} authorities 该接口需要的权限,默认是全部用户
 * @description 权限控制handler
 */
export const permissionHandler: PermissionHandler = (
  req,
  res,
  next,
  authorities = All_AUTHORITY
) => {
  const token = req.headers[HEADER_TOKEN_KEY.toLowerCase()] as string;
  jwt.verify(token, SECRET_KEY, function (err, decoded) {
    const tokenParams = decoded as TokenParams;
    if (authorities.includes(tokenParams.authority)) {
      next();
    } else {
      res
        .status(RespCode.NO_ACCESS)
        .send(new BaseResponse("No access", false, RespCode.NO_ACCESS));
    }
  });
};

总结

按照上述的方式,我们就完成了 express 搭建的后台应用的登录和接口鉴权操作,以及做到了接口级别的权限控制。

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值