授权
权限应该在何处检查?
在决定检查权限的位置时,应通过在不同层级实施多重检查来应用纵深防御。从低层级(如 finders 和 services)开始,然后是高层级,如 GraphQL、公共 REST API 和 controllers。
有关更多信息,请参阅 重用抽象指南。
在多个点保护相同的资源意味着,如果某一层防御被攻破或缺失,客户数据仍会被其他额外层保护。
有关权限的更多信息,请参阅 安全编码指南 中的权限部分。
注意事项
Services 或 finders 是合适的位置,因为:
- 多个端点共享 services 或 finders,因此下游逻辑更可能被重用。
- 有时授权逻辑必须融入数据库查询以过滤记录。
- 除了提供更好的用户体验外,您应该避免在显示层进行权限检查,而不是作为安全检查。例如,显示和隐藏像按钮这样的非数据元素。
纵深防御的缺点是:
DeclarativePolicy规则相对高效,但条件可能会执行数据库调用。- 更高的维护成本。
例外情况
开发者在权衡其特定情况的风险和缺点后,可以选择只在一个区域进行授权。
在做出例外时,优先选择域逻辑(services 或 finders)作为事实来源。
某些逻辑,如后端 worker 逻辑,可能不需要基于当前用户的授权。如果 service 或 finder 的构造函数不期望 current_user,那么它通常不检查权限。
前端
在 UI 元素中使用能力检查时,确保对底层后端代码也使用能力检查(如果存在)。这确保在用户拥有适当权限之前,绝对无法使用该功能。
如果 UI 元素是 HAML,您可以使用嵌入式 Ruby 来检查 Ability.allowed?(user, action, subject)。
如果 UI 元素是 JavaScript 或 Vue,请使用 push_frontend_ability 方法,该方法对所有继承自 ApplicationController 的控制器都可用。您可以使用此方法来暴露能力,例如:
before_action do
push_frontend_ability(ability: :read_project, resource: @project, user: current_user)
end然后,您可以在 JavaScript 中按如下方式检查能力状态:
if ( gon.abilities.readProject ) {
// ...
}JavaScript 中的能力名称始终是 camelCase 格式,因此检查 gon.abilities.read_project 是行不通的。
要在 Vue 模板中检查能力,请参阅 Vue 中访问能力的开发者文档。
提示
如果一个类接受 current_user,那么它可能负责授权。
示例:添加新的 API 端点
默认情况下,我们在端点处进行授权。检查现有能力可能是合理的;如果不是,那么我们可能需要添加一个。
顺便一提,大多数端点可以清晰地归类为资源的 CRUD(创建、读取、更新、删除)操作。services 和 abilities 也遵循这一模式,这就是为什么许多命名类似于 Projects::CreateService 或 :read_project。
例如,假设我们将整个端点提取到一个 service 中。can? 检查现在将在 service 中。假设该 service 重用了一个现有的 finder,我们正在为我们的目的修改它。我们应该让 finder 检查能力吗?
- 如果 finder 不接受
current_user,因此不检查权限,那么可能不需要。 - 如果 finder 接受
current_user,但不检查权限,那么您应该仔细检查 finder 的其他使用情况,并考虑添加授权。 - 如果 finder 接受
current_user,并且已经检查权限,那么我们需要添加我们的情况,或者现有的检查是合适的。