Rails: 在rails 中有什么方法可以建立基于动态角色的授权吗?
Rails: Is there any way to build dynamic role based authorization in rails?
我正在尝试在 Rails 中实现基于角色的授权。
我们的要求:
- 角色应该是动态的,我们应该能够创建、编辑或删除角色。
- 权限也应该是动态的。
调查结果:
- 我们无法使用
pundit
gem,因为它的策略是静态的,我们无法使其动态化。
- 我们可以使用
cancan
gem 并且我们可以动态使用它,但我不知道如何实现?以及它如何与数据库一起工作?
这是我在授权部分的第一个项目。我们有 rails 作为后端,vue.js 作为前端。无论角色是什么,数据库中的所有数据一开始都应该是空的。我们将使用种子创建超级管理员角色并授予所有权限。超级管理员会创建角色,编辑角色,销毁角色,最终还会添加权限,编辑和销毁权限。
如果有任何其他有用的方法,请告诉我。
谢谢。
如果我正确理解您的要求,您应该可以使用 Pundit 来实现。
据我了解,
- 用户有角色
- 可以在运行时为用户分配和取消分配角色
- 权限直接授予角色或用户。
- 权限可以在运行时更新
所以你可以有类似的东西,
class User
has_many :user_role_mappings
has_many :roles, through: :user_role_mappings
has_many :permissions, through: :roles
...
end
class UserRoleMapping
belongs_to :user
belongs_to :role
end
class Role
has_many :role_permission_mappings
has_many :permissions, through :role_permission_mappings
...
def has_permission?(permission)
permissions.where(name: permission).exists?
end
end
class RolePermissionMapping
belongs_to :role
belongs_to :permission
end
class Permission
...
end
并且在您的策略中,您可以检查用户的任何角色是否具有所需的权限。
class PostPolicy < ApplicationPolicy
def update?
user.roles.any? { |role| role.has_permission?('update_post') }
end
end
编辑:
使用映射表,您可以更新角色的权限,以及来自管理仪表板的用户的角色。
您可以使用 pundit
实现动态角色。 pundit
让我们定义普通的 Ruby 对象,其方法被调用以确定用户是否有权执行某项操作。例如:
class PostPolicy < ApplicationPolicy
def update?
user.has_role('admin')
end
end
如果您希望一个用户拥有多个角色,您可以在您的 User
模型上设置一个 has_and_belongs_to_many: :roles
关联。
参见:https://guides.rubyonrails.org/association_basics.html#the-has-and-belongs-to-many-association
您应该创建 has_many 角色与用户或您使用的任何模型的关联,然后轻松实现动态角色授权。
专家与 CanCanCan
你们关于CanCanCan和Pundit的结论都是胡说八道。它们都不是 "static" 或 "dynamic",它们具有几乎相同的功能。尽管架构和设计理念完全不同。
CanCanCan(最初是 CanCan)是作为 DSL 编写的,这是自 10 年前 Ryan Bates 创建 CanCan 时预切片面包以来最热门的东西。它可以很好地缩小规模并且易于学习,但是一旦达到任何复杂程度就会变得非常丑陋。如果在 CanCanCan 中做任何 "dynamic authorization" 的事情都会因为它的架构而成为一场噩梦。 CanCanCan中的能力class是所有神物中的神
Pundit 只是面向对象编程。在 Pundit 中,您的策略只是 classes,它们将用户和资源作为初始化参数并响应 show?
、create?
等方法。Pundit 最初更难理解,但因为它只是 OOP你可以随心所欲地定制它。由于您的身份验证逻辑存储在单独的对象中,因此它可以更好地扩展到复杂性并遵守 SOLID 原则。
如何设置动态角色系统?
这是你的标准角色系统 ala Rolify:
class User < ApplicationRecord
has_many :user_roles
has_many :roles, through: :user_roles
def has_role?(role, resource = nil)
roles.where({ name: role, resource: resource }.compact).exists?
end
def add_role(role, resource = nil)
role = Role.find_or_create_by!({ name: role, resource: resource }.compact)
roles << role
end
end
# rails g model user_roles user:belongs_to role:belongs_to
class UserRole < ApplicationRecord
belongs_to :user
belongs_to :role
end
# rails g model role name:string resource:belongs_to:polymorphic
class Role < ApplicationRecord
belongs_to :resource, polymorphic: true, optional: true
has_many :user_roles
has_many :users, through: :user_roles
end
然后您可以将角色范围限定为资源:
class Forum < ApplicationRecord
has_many :roles, as: :resource
end
Rolify 让您更进一步,只需定义带有 class 的角色作为资源。例如 user.add_role(:admin, Forum)
使用户成为所有论坛的管理员。
如何创建权限系统?
一个简单的 RBAC 系统可以构建为:
class Role < ApplicationRecord
has_many :role_permissions
has_many :permissions, through: :role_permissions
def has_permission?(permission)
permissions.where(name: permission).exists?
end
end
# rails g model permission name:string
class Permission < ApplicationRecord
end
# rails g model role_permission role:belongs_to permission:belongs_to
class RolePermission < ApplicationRecord
belongs_to :role
belongs_to :permission
end
例如,您可以通过以下方式在 Forum.find(1)
上将 "destroy" 授予 "moderators":
role = Role.find_by!(name: 'moderator', resource: Forum.find(1))
role.permissions.create!(name: 'destroy')
role.has_permission?('destroy') # true
虽然我怀疑它在现实中真的会这么简单。
我正在尝试在 Rails 中实现基于角色的授权。
我们的要求:
- 角色应该是动态的,我们应该能够创建、编辑或删除角色。
- 权限也应该是动态的。
调查结果:
- 我们无法使用
pundit
gem,因为它的策略是静态的,我们无法使其动态化。 - 我们可以使用
cancan
gem 并且我们可以动态使用它,但我不知道如何实现?以及它如何与数据库一起工作?
这是我在授权部分的第一个项目。我们有 rails 作为后端,vue.js 作为前端。无论角色是什么,数据库中的所有数据一开始都应该是空的。我们将使用种子创建超级管理员角色并授予所有权限。超级管理员会创建角色,编辑角色,销毁角色,最终还会添加权限,编辑和销毁权限。
如果有任何其他有用的方法,请告诉我。
谢谢。
如果我正确理解您的要求,您应该可以使用 Pundit 来实现。
据我了解,
- 用户有角色
- 可以在运行时为用户分配和取消分配角色
- 权限直接授予角色或用户。
- 权限可以在运行时更新
所以你可以有类似的东西,
class User
has_many :user_role_mappings
has_many :roles, through: :user_role_mappings
has_many :permissions, through: :roles
...
end
class UserRoleMapping
belongs_to :user
belongs_to :role
end
class Role
has_many :role_permission_mappings
has_many :permissions, through :role_permission_mappings
...
def has_permission?(permission)
permissions.where(name: permission).exists?
end
end
class RolePermissionMapping
belongs_to :role
belongs_to :permission
end
class Permission
...
end
并且在您的策略中,您可以检查用户的任何角色是否具有所需的权限。
class PostPolicy < ApplicationPolicy
def update?
user.roles.any? { |role| role.has_permission?('update_post') }
end
end
编辑:
使用映射表,您可以更新角色的权限,以及来自管理仪表板的用户的角色。
您可以使用 pundit
实现动态角色。 pundit
让我们定义普通的 Ruby 对象,其方法被调用以确定用户是否有权执行某项操作。例如:
class PostPolicy < ApplicationPolicy
def update?
user.has_role('admin')
end
end
如果您希望一个用户拥有多个角色,您可以在您的 User
模型上设置一个 has_and_belongs_to_many: :roles
关联。
参见:https://guides.rubyonrails.org/association_basics.html#the-has-and-belongs-to-many-association
您应该创建 has_many 角色与用户或您使用的任何模型的关联,然后轻松实现动态角色授权。
专家与 CanCanCan
你们关于CanCanCan和Pundit的结论都是胡说八道。它们都不是 "static" 或 "dynamic",它们具有几乎相同的功能。尽管架构和设计理念完全不同。
CanCanCan(最初是 CanCan)是作为 DSL 编写的,这是自 10 年前 Ryan Bates 创建 CanCan 时预切片面包以来最热门的东西。它可以很好地缩小规模并且易于学习,但是一旦达到任何复杂程度就会变得非常丑陋。如果在 CanCanCan 中做任何 "dynamic authorization" 的事情都会因为它的架构而成为一场噩梦。 CanCanCan中的能力class是所有神物中的神
Pundit 只是面向对象编程。在 Pundit 中,您的策略只是 classes,它们将用户和资源作为初始化参数并响应 show?
、create?
等方法。Pundit 最初更难理解,但因为它只是 OOP你可以随心所欲地定制它。由于您的身份验证逻辑存储在单独的对象中,因此它可以更好地扩展到复杂性并遵守 SOLID 原则。
如何设置动态角色系统?
这是你的标准角色系统 ala Rolify:
class User < ApplicationRecord
has_many :user_roles
has_many :roles, through: :user_roles
def has_role?(role, resource = nil)
roles.where({ name: role, resource: resource }.compact).exists?
end
def add_role(role, resource = nil)
role = Role.find_or_create_by!({ name: role, resource: resource }.compact)
roles << role
end
end
# rails g model user_roles user:belongs_to role:belongs_to
class UserRole < ApplicationRecord
belongs_to :user
belongs_to :role
end
# rails g model role name:string resource:belongs_to:polymorphic
class Role < ApplicationRecord
belongs_to :resource, polymorphic: true, optional: true
has_many :user_roles
has_many :users, through: :user_roles
end
然后您可以将角色范围限定为资源:
class Forum < ApplicationRecord
has_many :roles, as: :resource
end
Rolify 让您更进一步,只需定义带有 class 的角色作为资源。例如 user.add_role(:admin, Forum)
使用户成为所有论坛的管理员。
如何创建权限系统?
一个简单的 RBAC 系统可以构建为:
class Role < ApplicationRecord
has_many :role_permissions
has_many :permissions, through: :role_permissions
def has_permission?(permission)
permissions.where(name: permission).exists?
end
end
# rails g model permission name:string
class Permission < ApplicationRecord
end
# rails g model role_permission role:belongs_to permission:belongs_to
class RolePermission < ApplicationRecord
belongs_to :role
belongs_to :permission
end
例如,您可以通过以下方式在 Forum.find(1)
上将 "destroy" 授予 "moderators":
role = Role.find_by!(name: 'moderator', resource: Forum.find(1))
role.permissions.create!(name: 'destroy')
role.has_permission?('destroy') # true
虽然我怀疑它在现实中真的会这么简单。