权限管理pundit

作者: yaya_pangdun | 来源:发表于2016-06-16 19:19 被阅读597次

    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
    

    相关文章

      网友评论

        本文标题:权限管理pundit

        本文链接:https://www.haomeiwen.com/subject/aygmdttx.html