Passport JS 管理员用户验证

Passport JS admin user verification

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

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

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

我能想出的最好的方法来验证我的管理员用户是否是试图访问该路由的人是通过 mongo db 中的用户 ID 和 req.user._id

检查

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

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

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

授权可能有多种形式,但实际上又是一个布尔值:此用户是否符合访问此资源的条件。

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

这整个对话在角色和权限讨论中可能相当深入。

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

答案将在某处:完成此任务的最简单方法是什么?

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

一个 isAdmin 中间件是个好主意,但将它简单地作为一个函数放在 每个 需要该管理员的端点中也是一个好主意-查看。哪种方式更好?那么首先,哪种方式更简单。代码更少,但仍然很容易理解。

因为这是角色和权限,是否有更可靠的方法来跟踪哪些用户是管理员?如果你有像 if (req.user._id === 12345) {} 这样运行的代码,它需要特殊的知识来记住代码中的这个地方,所以它有点脆弱并且 "more likely" 会失败。也许为 is_admin 的用户 table 添加一列是个好主意,对于除您的用户之外的每个用户可能 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");
}

您也可以做一些事情,例如将 is_admin 数据库列更改为 role 之类的东西,对于除了您的管理员用户之外的每个用户,它可能是 1 可能 2。这将允许你做类似的事情:

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

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

在我看来,这种增加特权的想法很棒,但关键缺陷可能会在您重构路由或角色时出现。你必须记住你有 > 3 的所有地方并将其更改为 > 4。如果您忘记了任何内容,那将立即成为一种安全漏洞,所以我相信您能理解我的论点。

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

if ((req.user.role === 'ADMIN') || (req.user.role === 'MANAGER')) {}

我们必须不断回到这个想法:什么是最简单的?制作一个 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')
})

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

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' })
})

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

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