1. 原理
對應關系.png
Permission定義兩個字段:
action
和resource
。分別與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
里。
另外還需要兩個方法policies
和resource
:
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
其中user
和record
來自于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