You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何通过CanCanCan获取用户的全部动态权限(含分支权限)?

问题

我正在编写API,需要向API用户暴露包含ID及其他信息的所有可用权限。现有如下模型结构:

class User < ApplicationRecord
  has_many :permissions, dependent: :destroy
end

class Permission < ApplicationRecord
  belongs_to :user, dependent: :destroy
end

对应的Ability类:

class Ability
  include CanCan::Ability

  def initialize(user)
    user_permissions(user)
    dynamic_permissions(user)
  end

  def user_permissions(user)
    can :manage, :dashboard if user.admin?
  end

  def dynamic_permissions(user)
    user.permissions.each do |permission|
      #...
      next branch_permissions(permission) if permission.subject_class == 'branch'
    end
  end

  def branch_permissions(permission)
    can permission.action.to_sym, Branch, id: permission.allowed_ids
  end
end

我希望在控制器中获取该用户所有的can和cannot权限,查阅文档后使用#permissions方法,但它无法识别为Branch设置的动态权限。

期望的结果格式(伪代码):

Ability.new(User.find(params[:id])).dynamic_permissions =>
# {:can=>{:manage=>{"dashboard"=>[]}, :read=>{"Branch"=>['id1', 'id2']}, :index=>{"Branch"=>['id1', 'id2']}, :show=>{"Branch"=>['id1', 'id2']}}, :cannot=>{}}

但当前#permissions仅返回硬编码的静态权限:

{:can=>{:manage=>{"dashboard"=>[]}, :read=>{"Branch"=>[]}, :index=>{"Branch"=>[]}, :show=>{"Branch"=>[]}}, :cannot=>{}}

我知道可以编写自定义方法,但想了解官方认可的实现方式。

官方认可的解决方案

CanCanCan的#permissions方法是简化的权限摘要,仅返回动作和主体类型,不会解析规则中附带的条件(比如你代码里的id: permission.allowed_ids)。官方推荐直接操作Ability实例的rules集合,从中提取完整权限信息。

步骤1:解析Ability的rules集合

每个CanCan::Rule实例包含权限的动作、主体、条件等完整信息,遍历该集合即可提取所需字段:

def user_permissions_details(user)
  ability = Ability.new(user)
  result = { can: {}, cannot: {} }

  ability.rules.each do |rule|
    action_key = rule.base_behavior ? :can : :cannot
    actions = rule.actions.is_a?(Array) ? rule.actions : [rule.actions]
    subject = rule.subject_type || rule.subject_class.to_s
    conditions = rule.conditions || {}

    actions.each do |action|
      result[action_key][action] ||= {}
      # 提取allowed_ids,无则返回空数组
      allowed_ids = conditions[:id] || []
      result[action_key][action][subject] = allowed_ids
    end
  end

  result
end

步骤2:在控制器中调用

用这个方法就能得到包含ID列表的完整权限:

def show
  user = User.find(params[:id])
  permissions = user_permissions_details(user)
  render json: permissions
end

关键参数说明

  • rule.base_behavior:判断当前规则是can还是cannot类型
  • rule.actions:获取权限对应的动作(可能是单个动作或数组)
  • rule.subject_class/rule.subject_type:获取权限对应的主体类名称
  • rule.conditions:获取权限的条件哈希,从中提取allowed_ids

这种方式是官方文档推荐的自定义权限序列化方案,因为rules是Ability内部存储所有权限规则的核心集合,能完整保留所有权限细节。

内容的提问来源于stack exchange,提问作者zhisme

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.17 09:57:48