Rails 我的用户权限控制系统实现方式

lilijreey · 2016年10月27日 · 最后由 menpar 回复于 7 天前 · 3017 次阅读

需求:简单,正交,用户可完全控制

我没有使用任何 Gem 实现用户权限控制功能。因为那些 Gem 我不能一下就理解,貌似也满足不了我的需求

说干就干,这里参考学习了 the_role 这个 gem. 也是基于角色 -> 资源/动作来抽象整个权限控制 先来看个场景,有 Articles 和 Comments 这两种 resource, 一个 Aricle 可以有多个 Comments, 我们需要 让 Aritcle 的创建者可以删除隶属于这个 Aritcle 的所有 Comments, 而其他人只能删除自己的 Aritile,

class CommentsController < ApplicationController
  before_action  :set_comment, :_checkOwnerIfNeed, only: [:show, :edit, :update, :destroy]

  permissions :login, [:new, :create, :show, :index ]
  permissions :owner, [:edit, :update]
  permissions :belongs, [:destroy] ## belongs 说明destroy 操作不仅owner有权限执行, 这个comment所从属的Aritcle 的owner 也可以
  ## 注意这些设置只是默认值, 用户可以动态的为每个action重写定义权限
end

module ActionPermisson
  ##给action 设定默认权限
  def permission(action, option = :root)
    name = self.name.sub /Controller/, '' # MyBooksController -> my_books
    Rails.application.defActionPermission(name.underscore, action.to_s, option.to_s)
    logger.debug("defined action permisson: #{name}:#{action} #{option}")
  end

  # permissions :all, [:action1, :action2,...]
  def permissions(option, actions)
    actions.each { |a| permission(a, option) }
  end

  class DenyError < StandardError; end ## 权限不足异常

end


class ApplicationController < ActionController::Base
  protect_from_forgery with: :exception
  before_action :_permissionAllow? ## 用来检测权限


  extend ActionPermisson

  rescue_from ActionPermisson::DenyError do |e|
    logger.debug 'permission deny'
    render plain:'权限不足', status:403
  end

  private
  def _permissionAllow?
    if _checkPermisson
      true
    else
      raise ActionPermisson::DenyError
    end
  end

  def _checkPermisson
    controller = request[:controller]
    action = request[:action]
    if !current_user
      return Rails.application.default_permissions[controller].try(:[], action) == 'all'
    end

    if @current_user.id == 1 or @current_user.role.id == 1
      return true; ## 系统管理员
    end


    option = @current_user.role_rules[controller].try(:[], action)

    case option
    when 'allow' then true
    when 'owner', 'belongs'
      puts 'permission is owner'
      @checkOwner = option
    when 'deny'  then false
    when nil  ## 取默认值
      case option1 = Rails.application.default_permissions[controller].try(:[], action)
      when 'all', 'login'then true
      when 'owner', 'belongs'
        puts 'defalut permission is owner'
        @checkOwner = option1 
      when 'root' then false
      when nil then false
      else
        raise '不应该执行这里'
      end
    end
  end

  def _checkOwnerIfNeed
    return unless @checkOwner
    return if current_user.isOwner?(@_resource)


    if @checkOwner == :owner
      raise ActionPermisson::DenyError 
    end


    ## 向上查找
    obj = @_resource.try(:belongs)
    while (obj)
      if current_user.isOwner?(obj)
        return
      end
      obj = obj.try(:belongs)
    end

    raise ActionPermisson::DenyError 
  end

系统中有一个 role 表,用来存储不同角色的权限定义

每个用户有一个 role_id 来关联一个角色(同一时刻只能关联一个)

最后看一下 owner 权限的实现,owner 的意思是,只有所属人才能执行操作. 所以对 owner 的实现,并不能仅仅通过权限查表,必须要检测当前操作的资源, 也就是在查找资源后进行,通过设置

before_action  :set_comment, :_checkOwnerIfNeed, only: [:show, :edit, :update, :destroy]

我们看到首先 set_comment 会查找当前使用的资源 @comment, 然后在_checkOwnerIfNeed 中,我们检测是否是 owner 权限,如果是我们才检测用户是否为 owner, 这里检测的函数,并不是侵入式的,所有我们必须对检测函数同一资源命名,这里我们把需要检测的资源同一命名为!@_resource 这样就可以分类权限检测罗辑和正常罗辑

最后是 belongs 权限,他的意思是当前资源的owner,和当前资源的从属对象的 owner, 以及从属对象的从属对象的 owner, 一直向上,都有权限进行操作. 我把这种关系叫做#从属链# 也就是当前资源的从属链上的任何一个 owner 都可以进行操作. 具体的实现在 _checkIfNeedOwner 中,请大家自行查看. 为了查找从属链,需要在有从属关系的 Modle 上定义 belongs 这个函数,返回当前资源的从属资源 通过用一天实现这个原型,我成功的达到了我的实现目标. 很简单也很清晰.

As the game continues to gain popularity, it has created a community of enthusiasts who find joy in the camaraderie, creativity, and pure fun that A Small World Cup brings to their tabletops.

The intellectual depth and sophistication of your article are on Escape Road full display, as you navigate complex theoretical frameworks and apply them to real-world contexts, offering readers a comprehensive understanding of the topic.

需要 登录 后方可回复, 如果你还没有账号请 注册新账号