Sails 中的策略是用于授权和访问控制的多功能工具:它们允许你在运行操作之前执行一些逻辑,以确定是否继续处理请求。策略最常见的用例是将某些操作限制为仅限登录用户。
注意:策略仅适用于控制器和操作,不适用于视图。如果你在你的routes.js 配置文件中定义了一个直接指向视图的路由,则不会对其应用任何策略。为了确保应用策略,你可以定义一个显示视图的操作,然后将你的路由指向该操作。
最好避免在你的应用程序中实现大量或复杂的策略。相反,在实现诸如细粒度、基于角色的权限等功能时,依赖你的操作来拒绝不需要的访问。你的操作还应负责对响应中发送的视图局部变量和 JSON 响应数据进行任何必要的个性化。
例如,如果你需要在你的应用程序中实现用户级或基于角色的权限,最简单的解决方案是在控制器操作的顶部处理相关的检查——无论是内联还是通过调用辅助函数。遵循此最佳实践将显著提高代码的可维护性。
Sails 在config/policies.js
中有一个内置的 ACL(访问控制列表)。此文件用于将策略映射到操作和控制器。
此文件是声明式的,这意味着它描述了你的应用程序的权限应该是什么样子,而不是如何工作。这使得新开发人员更容易理解正在发生的事情,并且随着需求不可避免地随着时间的推移而发生变化,它使你的应用程序更具灵活性。
config/policies.js
文件是一个字典,其属性和值根据你是将策略应用于控制器还是独立操作而有所不同。
要将策略应用于控制器,请使用控制器名称作为config/policies.js
字典中属性的名称,并将它的值设置为一个字典,该字典将该控制器中的操作映射到应该应用于它们的策略。使用*
表示“所有未映射的操作”。策略的名称与其文件名相同,减去文件扩展名。
module.exports.policies = {
UserController: {
// By default, require requests to come from a logged-in user
// (runs the policy in api/policies/isLoggedIn.js)
'*': 'isLoggedIn',
// Only allow admin users to delete other users
// (runs the policy in api/policies/isAdmin.js)
'delete': 'isAdmin',
// Allow anyone to access the login action, even if they're not logged in.
'login': true
}
};
要将策略应用于一个或多个独立操作,请使用操作路径(相对于api/controllers
)作为config/policies.js
字典中的属性名称,并将值设置为应该应用于这些操作的策略。通过在操作路径的末尾使用通配符*
,你可以将策略应用于以该路径开头的所有操作。以下是上面相同的策略集,重写为应用于独立操作
module.exports.policies = {
'user/*': 'isLoggedIn',
'user/delete': 'isAdmin',
'user/login': true
}
请注意,此示例与基于控制器的策略略有不同,因为
isLoggedIn
策略将应用于api/controllers/user
文件夹及其子文件夹中的所有操作(user/delete
和user/login
除外,如下一节所述)。
需要注意的是,策略不会级联。在上面的示例中,isLoggedIn
策略将应用于UserController.js
文件(或位于api/controllers/user
下的独立操作)中的所有操作,除了delete
和login
。如果你希望将多个策略应用于一个操作,请将这些策略列在一个数组中。例如
'getEncryptedData': ['isLoggedIn', 'isInValidRegion']
Sails 的内置蓝图 API是使用常规 Sails 操作实现的。唯一的区别是蓝图操作是隐式的。
要将你的策略应用于蓝图操作,请像我们在上面的示例中所做的那样设置你的策略映射,但指向控制器中相关隐式蓝图操作的名称(或作为独立操作)。例如
module.exports.policies = {
UserController: {
// Apply the 'isLoggedIn' policy to the 'update' action of 'UserController'
update: 'isLoggedIn'
}
};
或者
module.exports.policies = {
'user/update': 'isLoggedIn'
};
你可以通过使用*
属性将策略应用于所有未明确映射的操作。例如
module.exports.policies = {
'*': 'isLoggedIn',
'user/login': true
};
这会将isLoggedIn
策略应用于除api/controllers/user/login.js
(或api/controllers/UserController.js
)中的login
操作之外的每个操作。
Sails 提供了两种内置策略,可以全局应用或应用于特定的控制器或操作
true
:公共访问(允许任何人访问映射的控制器/操作)false
:禁止访问(不允许任何人访问映射的控制器/操作)
'*': true
是所有控制器和操作的默认策略。在生产环境中,最好将其设置为false
,以防止访问你可能无意中公开的任何逻辑。
这是一个简单的isLoggedIn
策略,用于防止未经身份验证的用户访问。它检查会话中是否存在userId
属性,如果找不到,则发送默认的forbidden
响应值。
// policies/isLoggedIn.js
module.exports = async function (req, res, proceed) {
// If `req.me` is set, then we know that this request originated
// from a logged-in user. So we can safely proceed to the next policy--
// or, if this is the last policy, the relevant action.
// > For more about where `req.me` comes from, check out this app's
// > custom hook (`api/hooks/custom/index.js`).
if (req.me) {
return proceed();
}
//--•
// Otherwise, this request did not come from a logged-in user.
return res.forbidden();
};