1. 原理
对应关系.png
Permission定义两个字段:action和resource。分别与Rails的Controller和Model对应。
Model关系定义
classUser<ActiveRecord::Basehas_and_belongs_to_many:roleshas_many:permissions,through::rolesend
classRole<ActiveRecord::Basehas_many:permissions,dependent::destroyhas_and_belongs_to_many:usersend
classPermission<ActiveRecord::Basebelongs_to:rolehas_many:users,through::roleend
定义权限
classApplicationPolicyclass<<selfdefactions@actions||=[]enddefpermit(action_or_actions)acts=Array(action_or_actions).collect(&:to_s)acts.eachdo|act|define_method("#{act}?"){can?act}endactions.concat(acts)endendprivatedefcan?(action)permission={action:action,resource:record.is_a?(Class)?record.name:record.class.name}user.permission.exists?(permission)endend
在ApplicationPolicy里定义一个permit方法(类方法)用来定义和保存权限点,can?方法用来做权限检查。然后就可以像这样声明权限点:
classResourcePolicy<ApplicationPolicypermit[:read,:create,:update,:destroy]end
这些Action就会被保存到ResourcePolicy.actions里。
另外还需要两个方法policies和resource:
classApplicationPolicyclass<<selfdefpolicies@policies||=Dir.chdir(Rails.root.join('app/policies'))doDir['**/*_policy.rb'].collectdo|file|file.chomp('.rb').camelize.constantizeunlessfile==File.basename(__FILE__)end.compactendenddefresourcename.chomp('Policy')endendend
分别用来获取所有的 Policy 和 每个 Policy 对应的 resource (这两个方法是通过简单的命名规则实现的, 灵活性会差一点).
2. 使用pundit
classApplicationController<ActionController::BaseincludePunditend
添加验证
$ rails g pundit:install
生成默认的policy文件,路径为app/policies/application_policy.rb
将policies目录放到rails的自动加载路径中:config/application.rb
moduleBuildAnApiRailsDemoclassApplication<Rails::Application+config.autoload_paths<<Rails.root.join('app/policies')endend
$ rails g pundit:policyuser
生成 app/policies/user_policy.rb为User模型进行权限验证。
classUserPolicy<ApplicationPolicyclassScope<Struct.new(:user,:scope)defresolvescope.allendendend
如果users_controller有这么一段
defupdate@user=Article.find(params[:id])#这里验证current_user对这个@user是否有权限@user.update_attributes(user_attributes)end
我们给UserPolicy中添加一个方法,来验证这个用户是否有这个权限。
classUserPolicy<ApplicationPolicy+defupdate?+returntrueifuser.admin?+returntrueifrecord.id==user.id+end+defshow?+returntrue+end+defcreate?+returntrue+end+defdestroy?+returntrueifuser.admin?+returntrueifrecord.id==user.id+endend
其中user和record来自于ApplicationPolicy。
然后在UsersController中添加验证
defupdate@user=User.find(params[:id])authorize@article,:update?@user.update_attributes(user_attributes)end
由于action_name和验证方法的名字相同,可以简写
defupdate@user=User.find(params[:id])authorize@article@user.update_attributes(user_attributes)end
这是,我们已经进行了权限验证,当用户不具备权限的时候回抛出错误,不能不处理,需要捕获错误进行处理。
classApplicationController<ActionController::BaseincludePunditrescue_fromPundit::NotAuthorizedError,with::user_not_authorizedprivatedefuser_not_authorizedredirect_to root_url,:alert=>"You don't have permission to those resources."endend
3人点赞
网友评论