權限管理pundit

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
最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容