ruby on rails

权限管理pundit

2016-06-16  本文已影响597人  yaya_pangdun

1. 原理

对应关系.png
Permission定义两个字段:actionresource。分别与Rails的Controller和Model对应。
Model关系定义
class User < ActiveRecord::Base
 has_and_belongs_to_many :roles
 has_many :permissions, through: :roles
end
class Role < ActiveRecord::Base
 has_many :permissions, dependent: :destroy
 has_and_belongs_to_many :users
end
class Permission < ActiveRecord::Base
 belongs_to :role
 has_many :users, through: :role
end

定义权限


class ApplicationPolicy
  class << self
    def actions
      @actions ||= []
    end
    
    def permit(action_or_actions)
      acts = Array(action_or_actions).collect(&:to_s)
      acts.each do |act|
        define_method("#{act}?") {can? act}
      end
      actions.concat(acts)
    end
  end

  private
  
  def can?(action)
    permission = {
      action: action,
      resource: record.is_a?(Class) ? record.name : record.class.name
    }
  user.permission.exists?(permission)
  end
end

ApplicationPolicy里定义一个permit方法(类方法)用来定义和保存权限点,can?方法用来做权限检查。然后就可以像这样声明权限点:

class ResourcePolicy < ApplicationPolicy
 permit [:read, :create, :update, :destroy]
end

这些Action就会被保存到ResourcePolicy.actions里。
另外还需要两个方法policiesresource:

class ApplicationPolicy
  class << self
    def policies
      @policies ||= Dir.chdir(Rails.root.join('app/policies')) do
        Dir['**/*_policy.rb'].collect do |file|
          file.chomp('.rb').camelize.constantize unless file == File.basename(__FILE__)
        end.compact
      end
    end
    
    def resource
      name.chomp('Policy')
    end
  end
end

分别用来获取所有的 Policy 和 每个 Policy 对应的 resource (这两个方法是通过简单的命名规则实现的, 灵活性会差一点).

2. 使用pundit

class ApplicationController < ActionController::Base
  include Pundit
end

添加验证

$ rails g pundit:install

生成默认的policy文件,路径为app/policies/application_policy.rb
将policies目录放到rails的自动加载路径中:config/application.rb

module BuildAnApiRailsDemo
  class Application < Rails::Application
+   config.autoload_paths << Rails.root.join('app/policies')
  end
end
$ rails g pundit:policy user

生成 app/policies/user_policy.rb为User模型进行权限验证。

class UserPolicy < ApplicationPolicy
  class Scope < Struct.new(:user, :scope)
    def resolve
      scope.all
    end
  end
end

如果users_controller有这么一段

def update
  @user = Article.find(params[:id])
   #这里验证current_user对这个@user是否有权限
  @user.update_attributes(user_attributes)
end

我们给UserPolicy中添加一个方法,来验证这个用户是否有这个权限。

class UserPolicy < ApplicationPolicy
+  def update?
+    return true if user.admin?
+    return true if record.id == user.id
+  end

+  def show?
+    return true
+  end

+  def create?
+    return true
+  end

+  def destroy?
+    return true if user.admin?
+    return true if record.id == user.id
+  end
end

其中userrecord来自于ApplicationPolicy。
然后在UsersController中添加验证

def update
 @user = User.find(params[:id])
 authorize @article, :update?
 @user.update_attributes(user_attributes)
end

由于action_name和验证方法的名字相同,可以简写

def update
  @user = User.find(params[:id])
  authorize @article
  @user.update_attributes(user_attributes)
end

这是,我们已经进行了权限验证,当用户不具备权限的时候回抛出错误,不能不处理,需要捕获错误进行处理。

class ApplicationController < ActionController::Base
  include Pundit
  rescue_from Pundit::NotAuthorizedError, with: :user_not_authorized

  private

  def user_not_authorized
    redirect_to root_url, :alert => "You don't have permission to those resources."
  end
end
上一篇下一篇

猜你喜欢

热点阅读