Passport JS 管理员用户验证

Ian*_*Ian 3 ejs node.js express passport.js

我有一个前端带有 EJS 的 Node Express 应用程序。

我有一个带护照的中间件功能,它在所有创建、编辑、删除路由之前运行。

function isLoggedIn(req, res, next) {
    if (req.isAuthenticated()) {
        if (req.user._id != "12345") {
            res.redirect("/error");
        }
        return next();
    }
    res.redirect("/error");
}
Run Code Online (Sandbox Code Playgroud)

我能想到的最好的方法是通过 mongo db 中的用户 id 和 req.user._id 检查我的管理员用户是否是尝试访问该路由的用户

有没有更好的方法来处理管理员用户对路由和 html 组件的访问?

agm*_*984 7

这对我来说基本上是正确的。重要的是要注意有两层:authenticationauthorization

身份验证实际上是一个布尔值:用户是否经过身份验证?你在那里有你的职能req.isAuthenticated()。这可能在逻辑上返回一个布尔值,true 或 false,表示用户是否经过身份验证(即:登录)。

授权可能可以采取多种形式,但实际上又是一个布尔值:该用户是否满足访问该资源的条件。

身份验证通常在中间件中得到很好的服务,中间件在“端点”之前运行,但授权并不那么简单,因为任何端点都可以允许或拒绝操作,或者它可以根据用户的权限做出不同的响应。

整个对话可能非常深入地讨论角色和权限。

我认为答案取决于应用程序。您的应用程序有两个要求:一是用户必须经过身份验证,二是用户可能需要是管理员。

答案就在附近:实现这一目标的最简单方法是什么?

在我看来,您会考虑 SOLID 原则,并注意到您有一个中间件,因此它应该有一个职责:检查用户是否经过身份验证。接下来,也许您应该有另一个名为的中间件isAdmin,它为需要此额外条件的每个端点运行。这确实是全部——额外的检查。你不应该污染你的isLoggedIn这些额外的东西污染您的中间件,因为它会使中间件的可重用性和可组合性降低。

中间件将是一个好主意,但将其作为每个需要管理检查的端点isAdmin内的函数也可能是个好主意。哪种方式更好?首先,哪种方式更简单。虽然代码较少,但仍然很容易理解。

因为这是角色和权限,是否有更强大的方法来跟踪哪些用户是管理员?如果您的代码运行类似于if (req.user._id === 12345) {},则需要特殊知识来记住代码中的这个位置,因此它有点脆弱并且“更有可能”失败。也许在您的用户表中添加一列可能是一个好主意,该列is_admin可以是null0除了您的用户之外的每个用户都可以有1。那么你可以检查一下if (req.user.is_admin) {}

这可能会导致我们使用如下中间件函数:

function isAdmin(req, res, next) {
    if (req.isAuthenticated() && (req.user.is_admin === 1)) {
        return next();
    }
    return res.redirect(403, "/error");
}
Run Code Online (Sandbox Code Playgroud)

您还可以执行诸如更改is_admin数据库列之类的操作,以代替role可能适用1于每个用户的操作,除了管理员用户(可能有2. 这将允许你做类似的事情:

function hasAuthorization(req, res, next) {
    if (req.isAuthenticated() && (req.user.role >= 2)) {
        return next();
    }
    return res.redirect(403, "/error");
}
Run Code Online (Sandbox Code Playgroud)

这样的逻辑可以让你拥有越来越多的权限角色:也许 1 是常规角色,2 是经理,3 是管理员,4 是超级管理员。如果用户的角色少于4,则他们没有权限。

在我看来,这种增加特权的想法很棒,但当您重构路线或角色时,可能会出现严重缺陷。您必须记住您所拥有的所有位置> 3并将其更改为> 4. 如果你忘记了任何一个,这会立即成为一种安全缺陷,所以我相信你理解我的论点。

而不是看到像<and 这样的运算符>。我宁愿看到对特定角色的检查,例如:

if ((req.user.role === 'ADMIN') || (req.user.role === 'MANAGER')) {}
Run Code Online (Sandbox Code Playgroud)

我们必须不断回到这个想法:什么是最简单的?制作一个isAdmin中间件然后将所有管理路由分组到中间件下是否更简单?或者将授权检查放在每个路由中更简单吗?

在这里检查这个例子:

import isAdmin from '../auth/isAdmin.js'

app.get('/admin', (req, res) => {
    if (!isAdmin(req.user)) {
        return res.redirect(403, '/error')
    }

    return res.render('admin')
})
Run Code Online (Sandbox Code Playgroud)

这可能需要更多工作,但也可能更细粒度,因此您拥有更多控制权。

app.get('/foobars', (req, res) => {
    if (isAdmin(req.user)) {
        return res.json(/* all foobar records from all accounts */)
    }

    if (isManager(req.user)) {
        return res.json(/* all foobar records from the user's account */)
    }

    return res.json({ error: 'Insufficient privileges for this operation' })
})
Run Code Online (Sandbox Code Playgroud)

我最后的想法是,你应该有两个功能:一个检查用户是否经过身份验证,一个检查用户是否被授权。然后,您可以将它们堆叠在一个中间件或两个中间件中,或者在一个路由中。

我还认为你应该找到一种更强大的方法来检查用户是否是你自己。如果您将应用程序从一台计算机移动到另一台计算机,则下次填充用户表时用户 ID 可能会发生变化,因此这id不是锁定用户的有效方法。

  • 感谢您的精彩深入解释。这很有意义,实际上我已经在做类似的事情了,我制作了第二个中间件函数 isAdmin。很高兴听到我的想法是正确的。 (2认同)