You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在Express中间件中直接传入req对象内的自定义资源参数?

Express自定义授权中间件:传递动态资源参数的解决方案

问题场景

我在Express中编写自定义授权中间件,核心逻辑是对比req.user.id与目标资源的userId,验证用户是否拥有资源权限。当前实现只能通过传入字符串指定req上存储资源的属性,但希望能直接传入资源(尤其是嵌套较深的数据),比如调用verifyUserAuthorization(['owner'], req.invoices),但直接这么做会因为req在中间件初始化时未定义而报错。

当前的中间件代码:

function verifyUserAuthorization(roles, resource){
    return (req, res, next) => {
        try{
            // 验证登录用户是否为资源所有者:对比req.user.id与resource.userId
            if(roles.includes('owner')){
                // 处理资源为数组的情况:检查数组内所有资源的所有权
                if(Array.isArray(req[resource])){
                    let allAuthorized;
                    for(let item of req[resource]){
                        if(req.user.id !== item.userId){
                            allAuthorized = false;
                            break;
                        } 
                    }
                    if(allAuthorized !== false){
                        return next();
                    }
                // 处理单个资源的情况
                }else if(req.user.id === req[resource].userId){
                    return next();
                }
            }
            // 未找到有效授权角色时默认抛出401错误
            throw new Error("Access denied", {statusCode: 401});
        }catch(err){
            next(err);
        }
    }
};

当前调用方式(只能传字符串):

verifyUserAuthorization(['owner'], 'invoices')

解决方案:用函数延迟获取资源

核心问题是中间件初始化阶段(调用verifyUserAuthorization时),请求对象req还未生成,直接传入req.invoices必然报错。解决思路是传入一个资源获取函数,让函数在中间件实际执行时(此时req已存在)再去获取目标资源。

修改后的中间件代码

function verifyUserAuthorization(roles, resourceGetter) {
    return (req, res, next) => {
        try {
            // 兼容字符串和函数两种资源指定方式
            let resource;
            if (typeof resourceGetter === 'string') {
                // 保留原逻辑:从req上按属性名取资源
                resource = req[resourceGetter];
            } else if (typeof resourceGetter === 'function') {
                // 新增逻辑:执行函数获取资源,支持嵌套结构
                resource = resourceGetter(req);
            } else {
                throw new Error("Invalid resource parameter", { statusCode: 400 });
            }

            if (roles.includes('owner')) {
                // 批量验证数组资源:所有项必须属于当前用户
                if (Array.isArray(resource)) {
                    const allAuthorized = resource.every(item => req.user.id === item.userId);
                    if (allAuthorized) return next();
                } 
                // 验证单个资源
                else if (resource?.userId === req.user.id) {
                    return next();
                }
            }

            throw new Error("Access denied", { statusCode: 401 });
        } catch (err) {
            next(err);
        }
    };
}

新的调用方式

  • 原有字符串调用方式依然可用,兼容旧代码:
verifyUserAuthorization(['owner'], 'invoices')
  • 支持传入函数获取嵌套资源,解决深层数据的便捷访问:
// 访问嵌套资源:req.order.invoices
verifyUserAuthorization(['owner'], req => req.order.invoices)
// 访问单个嵌套资源:req.userProfile
verifyUserAuthorization(['owner'], req => req.userProfile)

代码改进点

  1. 延迟资源获取:通过函数避免初始化阶段访问未定义的req
  2. 兼容性:保留原字符串调用方式,无需修改现有业务代码
  3. 代码简化:用Array.every替代手动循环,更简洁高效
  4. 容错处理:用resource?.userId避免资源为undefined时的报错

内容的提问来源于stack exchange,提问作者Brenden

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.30 00:13:11