【问题标题】:How best to represent roles in the DB of a rails app?如何最好地表示 Rails 应用程序数据库中的角色?
【发布时间】:2015-10-23 21:23:25
【问题描述】:

我正在我的应用程序中建立授权并具有三个角色:

  • 用户
  • 版主
  • 管理员

在 Rails 中表示这一点的最佳方式是什么?我考虑过将以下布尔字段添加到我的用户表中:

  • is_user
  • is_moderator
  • is_admin

然后在User模型中创建is_user?等函数。

一个用户可以有多个角色(可以是三个角色)。

【问题讨论】:

标签: ruby-on-rails ruby postgresql ruby-on-rails-4


【解决方案1】:

我建议对您的访问级别使用按位值。这是有效的,因为您的角色数量有限,而且您的用户可以同时拥有多个角色。

与使用外部 gem 相比,它的维护速度更快、更容易。

想法是为每个访问级别使用一个数值,可以在位级别累积。

Level     | Int val | Bit val
user      |       1 | 00000001
moderator |       2 | 00000010
admin     |       4 | 00000100
another   |       8 | 00001000
etc ...   |      16 | 00010000

因此,用户和版主的访问级别为 1 + 2 = 3(位 00000011)。然后我们可以测试bit值来定义用户access_level


1) 在你的用户模型中添加一个access_level 列,类型为整数,默认为 0

2) 设置常量来定义您的访问级别值

class User < ActiveRecord::Base
  USER = 1
  MODERATOR = 2
  ADMIN = 4
end

3) 创建add_access_levelremove_access_level 方法

class User < ActiveRecord::Base
  # Add the desired bit
  def add_access_level(level)
    self.access_level |= level
  end
  # Removed the desired bit
  def remove_access_level(level)
    self.access_level &= ~level
  end
end

注意:由您决定将这些更改保留在数据库中

4) 创建is_user?is_moderator?is_admin? 方法

class User < ActiveRecord::Base
  def is_user?
    is_access_level? USER
  end
  def is_moderator?
    is_access_level? MODERATOR
  end
  def is_admin?
    is_access_level? ADMIN
  end

  private
    def is_access_level?(level)
      self.access_level & level == level
    end
end

注意:我会使用method_missing,但这超出了我们的范围。

5) 测试一下

u = User.new

u.is_admin?
=> false
u.is_moderator?
=> false
u.is_user?
=> false

u.add_access_level User::USER
u.add_access_level User::MODERATOR

u.is_admin?
=> false
u.is_moderator?
=> true
u.is_user?
=> true

u.remove_access_level User::MODERATOR

u.is_admin?
=> false
u.is_moderator?
=> false
u.is_user?
=> true

【讨论】:

  • 这是什么魔法!棒极了。谢谢!
【解决方案2】:

我喜欢接受的答案,但我只想分享我的愚蠢方式......

我的意思是我可以使用按位方法来解决问题,但是数值总是会消耗我的人类记忆(这个数字应该是什么?哪个角色有这个数字?如何使用这些二进制位完成计算?等等...)

所以我更喜欢一种更易读的方式......这是我的解决方案,灵感来自公认的答案(感谢作者):

注意:我假设您的 users 表中有一个字符串列 roles(我认为 PostgreSQL 有一个数据类型数组,但在某些情况下,我更喜欢使用简单的字符串列)强>

class User < ApplicationRecord
  
  ALLOWED_ROLES = ['user', 'moderator', 'admin']
  
  def get_roles
    current_roles = self.roles || '' # to prevent nil value returned
    current_roles.split(',')
  end
  
  def add_role(role)
    roles_arr = self.get_roles
    roles_arr << role unless roles_arr.include?(role)
    # clean up any other role that doesn't exist anymore in the allowed roles
    roles_arr = roles_arr.reject { |item| !ALLOWED_ROLES.include? item }
    self.roles = roles_arr.join(',')
  end
  
  def remove_role(role)
    roles_arr = self.get_roles
    roles_arr.delete(role)
    self.roles = roles_arr.join(',')
  end
  
  def has_role?(role)
    self.get_roles.include?(role)
  end

end

现在您可以执行以下操作:

user = User.first
user.add_role('admin')
user.has_role?('admin')
# => true

user.has_role?('moderator')
# => false
user.add_role('moderator')
user.has_role?('moderator')
# => true

user.get_roles
# => ["admin", "moderator"]

另外,请注意,这些方法只是为了操作内存中的字段,您仍然需要调用 user.save 将更改持久化到您的数据库中。

【讨论】:

    猜你喜欢
    • 2014-10-17
    • 1970-01-01
    • 2010-11-29
    • 2012-10-17
    • 2016-10-14
    • 1970-01-01
    • 1970-01-01
    • 2015-09-11
    • 1970-01-01
    相关资源
    最近更新 更多