如何使用 JWT 在 Express 中为单个路由(无代码重复)验证多种类型用户的身份验证令牌?

dum*_*der 5 node.js express jwt

我有一个快速应用程序,其中一些路线是公开的(互联网上的每个人都可以访问),而有些路线受到保护。我使用 JWT 进行路由认证。我在系统中有不同类型的用户,例如:作者、订阅者等。有些路由是访问作者,有些是订阅者。但是有一些路由可供作者和订阅者访问。

为了确保当用户请求受保护的路由时,我对每一类用户使用中间件功能来验证他们的身份验证令牌。

仅像下面这样的作者可以访问的示例路线,

router.get("/", verifyToken.author(), async (req, res) => {
    try {
        // doing stuff
    } catch (err) {
        // doing stuff
    }
});
Run Code Online (Sandbox Code Playgroud)

订阅者只能访问的示例路线,如下所示,

router.get("/", verifyToken.subscriber(), async (req, res) => {
    try {
        // doing stuff
    } catch (err) {
        // doing stuff
    }
});
Run Code Online (Sandbox Code Playgroud)

PS:verifyToken.author()或者我为了验证令牌而编写的任何其他中间件函数实际上都是闭包。因为有时我需要在函数中传递参数。所以,这些方法的一个例子是,

module.exposts.author = () => {
    return (req, res, next) => {
        const token = req.header("Authorization");
        if (!token) return res.status(401).send({ message: "Access Denied!" });

        try {
            jwt.verify(token, process.env.AUTHOR_SECRET);

            next();
        } catch (err) {
            res.status(401).send({ message: "Access Denied!" });
            console.log(err);
        }
    };
};
Run Code Online (Sandbox Code Playgroud)

当我需要编写一个函数来验证作者和订阅者的令牌时,我可以检查这样做并将该方法用作所需路由的中间件。但恐怕这并不像听起来那么简单,因为实际上我不只有这两种类型的用户。并且几乎所有用户类型都有复杂的路由访问,其中可能有 5 个特定类型的用户可以访问该路由。

正如我所说,假设作者和订阅者属于我上面所说的 5 种用户类型。因此,为作者+订阅者编写一个函数,并为作者、订阅者存在的 5 类用户编写另一个函数,似乎代码重复太多。

我希望我能解释为什么我不想写这样的东西,

module.exports.authorSubscriber = () => {
    return (req, res, next) => {
        const token = req.header("Authorization");
        if (!token) return res.status(401).send({ message: "Access Denied!" });

        try {
            jwt.verify(token, process.env.AUTHOR_SECRET);

            next();
        } catch (err) {
            try {
                jwt.verify(token, process.env.SUBSCRIBER_SECRET);
    
                next();
            } catch (err) {
                res.status(401).send({ message: "Access Denied!" });
                console.log(err);
            }
        }
    };
};
Run Code Online (Sandbox Code Playgroud)

相反,我想要这样的东西,在那里我可以重用我之前为作者编写的函数,

module.exports.authorSubscriber = () => {
    return (req, res, next) => {
        const token = req.header("Authorization");
        if (!token) return res.status(401).send({ message: "Access Denied!" });

        try {
            jwt.verify(token, process.env.AUTHOR_SECRET);

            next();
        } catch (err) {
            exports.author()
        }
    };
};
Run Code Online (Sandbox Code Playgroud)

但是,author()在这里行不通。它在从路由调用时工作,因为它会获取req, res, next. 但是在这种情况下,它是从另一个函数调用的,并且没有传递 req, res, next 。如果我exports.author(req, res, next)authorSubscriber和笔者定义接受这些参数。它仍然不起作用。我认为路由中间件会自动获取“req、res、next”,这些东西不能手动传递,是吗?这就是我关闭这些函数的原因,因为在类似的验证器上,我需要传递某个令牌,而中间件不需要额外的参数。

我很迷惑。您可能已经清楚地意识到我有知识差距,这就是为什么我在这里遗漏了一些东西。
请帮忙。

Myk*_*lis 1

假设您在闭包中定义“作者”中间件,如上面的示例所示:

module.exposts.author = () => {
    return (req, res, next) => {
        // implementation here
    };
};
Run Code Online (Sandbox Code Playgroud)

然后您确实可以在您描述的“作者+订阅者”场景中重复使用它。但请记住,上面定义的中间件不是名为 的函数author;相反,中间件是一个从名为 的函数返回的匿名函数author。因此,如果您想在 中重新使用它authorSubscriber,它看起来会是这样的:

module.exports.authorSubscriber = () => {
    return (req, res, next) => {
        // ...
        try {
            jwt.verify(token, process.env.AUTHOR_SECRET);

            next();
        } catch (err) {
            exports.author()(req, res, next)
        }
    };
};
Run Code Online (Sandbox Code Playgroud)

请注意,您首先调用exports.author(),这将返回对内部函数的引用,该内部函数是您实际想要调用的中间件。

正如其他评论所指出的,这种类型的访问控制可能有更好的模式,您可以在未来转向,但如果您的目标只是重用您已经在不同上下文中定义的中间件,我认为这似乎可行的。