我认为这份内容基本正确。需要注意的是,有两个层面:身份验证(authentication)和授权(authorization)。
身份验证实际上是一个布尔值:用户是否经过身份验证?你在这里有你的函数 req.isAuthenticated()
。它可能逻辑上返回一个布尔值,true或false,表示用户是否已经经过身份验证(即登录)。
授权可能采用多种形式,但实质上仍然是一个布尔值:该用户是否符合访问此资源的条件。
身份验证通常在中间件中很好处理,在“终点”之前运行,但授权并不像身份验证那样简单,因为任何终点都可以允许或拒绝操作,或者根据用户的特权以不同方式响应。
整个对话或许在角色和权限讨论中是非常深入的。
我认为答案取决于应用程序。您的应用程序有两个要求:一是用户必须通过身份验证,二是用户可能需要成为管理员。
答案大概会是:最简单的方法是什么?
我认为,您应该考虑SOLID原则并注意到您只有一个中间件,因此它应该具有一个职责:检查用户是否已通过身份验证。接下来,也许您应该有另一个名为 isAdmin
的中间件,它适用于每个需要此额外条件的终点。那就是这个中间件所做的所有事情——进行额外检查。您不应该在 isLoggedIn
中间件中添加这些额外内容,因为这会使该中间件的重用性和可组合性降低。
isAdmin
中间件可能是一个好主意,但也有可能只是将其作为需要管理员检查的每个终点内部的函数。哪种方式更好?首先,哪种方式更简单。哪种方式更少代码,但仍然简单易懂。
由于这涉及到角色和权限,是否存在更加强大的方法来跟踪哪些用户是管理员?如果你运行的代码类似于 if (req.user._id === 12345) {}
,那么就需要特殊知识来记住代码中的这个位置,因此它有点脆弱且“更容易”出错。也许向用户表添加一个 is_admin
列会是个好主意,它可以对每个用户设置为 null
或 0
,而你的用户可以设置为 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()
}
if (isManager(req.user)) {
return res.json()
}
return res.json({ error: 'Insufficient privileges for this operation' })
})
我的最终想法是,你应该有两个功能:一个检查用户是否已经认证,另一个检查用户是否有权限。然后你可以将它们堆叠在一起,无论是在中间件中还是在两个中间件中,或者在一个路由中。
我还认为你应该找到一种更强大的方法来检查用户是否是你自己。如果你把你的应用从一台电脑移动到另一台电脑,下次填充用户表时用户ID可能会更改,所以"id"不是锁定你的用户的强有力方式。
req.user
。我建议搜索类似“req.user是否安全”的内容。但一般来说,用户无法访问request
对象。那是服务器创建的一个不可变对象(除非你因某种原因改变了它,哈哈)。req
(请求)和res
(响应)通常是每个HTTP服务器中存在的两个对象,无论语言或框架如何。 - agm1984