Rails插件:CanCan权限验证插件学习总结
CanCan是rails下的一个用于限制用户对网站资源访问控制权限的插件,所有的权限都定义在一个文件中(ability.rb)。
1.安装
在gemfile中加上gem
‘cancan’
2.注意要点
注意:CanCan需要调用controller中的current_user方法来获取当前登录的用户对象,当然也允许用户修改这个方法名称,如下:
(1)
在ApplicationController中定义如下方法
private
def
current_ability
@current_ability ||=
AccountAbility.new(current_account)
#
上一句话将会将CanCan调用Ability类修改为调用AccountAbility类,并且通过current_account获取当前登录用户
end
(2)
修改Ability参数,在某些情况下会要根据用户及其他相关信息进行权限控制,如限制某个IP的用户访问,因为Ability类中没有request对象,因此需要从controller中传递给Ability,方法如下:(同理可用于session和cookie)
(A)
在ApplicationController中定义如下方法
private
def
current_ability
@current_ability ||= Ability.new(current_user,
request.remote_ip)
end
(B) Ability类如下:
class
Ability
include CanCan:Ability
def initialize(user,
ip_address)
can :create, Comment unless BLACKLIST_IPS.include?
ip_address
end
end
3.使用方法
(1)
定义Ability类,可以手动建立(model目录下),也可以通过命令行建立。
命令行方法为:rails
g cancan:ability
(2) 检测和认证权限的函数
(A)
在controller或view中使用can?,
cannot?
(B) authorize!方法只能用于controller中
(C)
可以在controler中使用load_and_authorize_resource或authorize_resource,本方法如要用于RESTful样式的controller中,它将会为所有action添加个before_filter来检测权限,不同的是load_and_authorize_resource会先加载本类model的值。
(3)
处理未授权的访问的方法
如果权限认证失败,cancan会抛出一个CanCan::AccessDenied的异常,你可以在ApplicationController中捕获它来显示自己内容。
rescue_from
CanCan::AccessDenied do |exception|
redirect_to root_url, :alert
=> exception.message
# exception.action,
exception.subject
end
(4)
设置对应用程序所有action都检查权限,可以在ApplicationController中添加check_authorization,如果在个别的controller中需要跳过验证,可以在该controller中添加skip_authorization_check。
注意:使用check_authorization可以跟
if 和
unless
条件,如:
check_authorization
if||unless) => :函数
4.如何定义Ability类
class
Ability
include CanCan::Ability
def initialize(user)
user
||= User.new # 防止用户未登录
if
…..# 判断当前用户是否时管理员
can
:manage, :all # 可以管理所有资源
else
can
:read, :all # 可以读取所有资源
end
end
end
(1) can函数
can
方法,
对象[,
对象ID]
这里的方法通常为:read,
:create, :update和:destroy,
但是它可以为任何方法。对象可以是model的对象或类名
(A)
可以通过alias_action来将几个方法合并成一个方法。如下:
class
Ability
include CanCan::Ability
def
initialize(user)
alias_action :update, :destroy, :to =>
:modify
can :modify, Comment
end
end
(B) 传递数组
can
[:update, :destroy], [Article, Comment]
(C) 根据条件判断权限
#
允许查看projects中active=true,并且user_id为当前登录用户的project
can
:read, Project, :active => true, :user_id => user.id
#
允许查看projects中belongs_to的category(visible
= true)的project
can
:read, Project, :category => {:visible => true}
#
允许查看projects中priority为1-3之间的project
can
:read, Project, :priority => 1..3
#
允许查看projects中belongs_to的group(group的id在用户所属group的id之中)的project
can
:read, Project, :group => {:id => user.group_ids}
(D)
根据block条件来判断权限
#
允许更新所有project.priority
< 3的project
can
:update, Project do |project|
project.priority < 3
end
注意:使用block时,内部只能用当前类的对象,如上个例子只能使用project对象,其他对象都不行,如user等。
5.controller中验证的函数
(1)
authorize! 方法,
对象
调用本方法检测无权限时,会抛出一个CanCan::AccessDenied异常
(2)
authorize_resource
本方法会自动将当前controller中所有action都加上before_filter来调用authorize!来检测权限
(3)
load_and_authorize_resource包含(load_resource和authorize_resource)
与authorize_resurce类似,只是会在验证之前根据controller的名字去自动生成对应的model的对象,如:
class
ProductsController < ActiveRecord::Base
load_and_authorize_resource def index # 注意生成对象的名字和controller是同名的
# @products =
Product.accessible_by(current_ability) end def show # @products =
Products.find(params[:id]) end end 6.controller中自定义检测对象的类名
load_and_authorize_resource
:class => ‘Store::Product’
6.controller中加载验证过程覆盖的方法
class
BooksController < ApplicationController before_filter
:find_my_book, nly =>
:show
load_and_authorize_resource
private
def
find_my_book
@book = Book.released.find(params[:id])
#
这里会将load_resource自动生成的@book换成自己的
end
end
注:如果只是使用了authorize_resource来验证权限,那么必须使用prepend_before_filter来调用自己的代码
7.当前用户更新自己的信息时,最好清空下ability和当前用户,防止有缓存存在
if
@user.update_attributes(params[:user])
@current_ability =
nil
@current_user = nil
end
8.无权限的异常捕捉
(1)
错误信息的自定义
(A)
authorize! :read, Article, :message => “Unable to read the
article.”
(B) raise CanCan::AccessDenied.new(“Not
authorized!”, :read, Article)
(C)
修改语言包config/locales/en.yml
en:
unauthorized:
manage:
all:
“Not authorized to %{action} %{subject}”
user: “Not allowed
to manage other user accounts”
update:
project: “Not
allowed to update the project”
9.Ability权限的调试方法
user
= User.first # fetch any user you want to test abilities on
project
= Project.first # any model you want to test against
ability =
Ability.new(user)
ability.can?(:create, project) # see if it
returns the expected behavior for that action
ability.can?(:index,
Project) # see if user can access the
class
Project.accessible_by(ability) # see if returns the records
the user can access
Project.accessible_by(ability).to_sql # see
what the generated SQL looks like to help determine why it’s not
fetching the records you want
另外可以将无权限的异常写入日志文件在rescue_from
CanCan:AccessDenied中添加:
Rails.logger.debug
“Access denied on #{exception.action} #{exception.subject.inspect}”
10.Ability权限设置的技巧
(1)
可以管理项目的相关信息,但是不能删除项目
can
:manage, Project
cannot :destroy, Project
(2)
可以管理自己的项目,但是不能编辑已经锁定的项目
can
:manage, Project, :user_id => user.id
can :update, Project do
|project|
!project.locked?
end
(3)
RESTful格式的action名称,有以下注意点:
:read
等于 :index,
:search, :show
:update 等于
:update,
:edit
:create 等于
:new,
:create
:delete 等于
:destroy,
:delete
11.根据用户权限获取数据
通常使用accessible_by(current_ability)来获取当前用户可以:read的信息,当然当使用了load_resource后,可以不需要自己获取。
也可以修改默认的:read,如accessible_by(current_ability,
:update)来获取当前用户可以编辑的数据
12.带角色的权限设置
(1)
一个用户一个角色,通过用户表中role的字段来存储,直接在Ability中用角色名称判断
(2)
多个用户对应多个角色,如:
(A)
用到的表如下
用户表:users
字段:name:string,
password:string
角色表:roles
字段:name:string
用户和角色关系表:users_roles_relations
字段:role_id:integer,
user_id:integer
权限表:permissions
字段:action:string,
subject_class:string, role_id:integer
(B) models
class
User
has_many :users_roles_relations
has_many :roles, :through
=> users_roles_relations
end
class Role
has_many
:users_roles_relations
has_many :users, :through =>
users_roles_relations
has_many :permissions
end
class
UsersRolesRelation
belongs_to :role
belongs_to :user
end
class
Permission
belongs_to :role
end
(C) ability
class
Ability
include CanCan::Ability
def initialize(user) #
这里的user是由cancan自动调用current_user来获取到的,最好定义在ApplicationController中
user
||= User.new
if user.roles.find_name(‘admin’) #
当前用户有角色名称为admin的角色时,有所有权限
can
:manage, :all
else
user.roles.each do
|role|
role.permissions.each do |permission|
can
permission.action.to_sym,
permission.subject_class.constantize
end
end
end
end
end
(D)
在controller中调用相应的验证函数就行了。
(E)
无权限异常的捕捉,上述也已讲过,只用在ApplicationController中添加rescue_from
CanCan::AccessDenied就行了