没写成 Gem,放在项目里头,下面链接里说了这个系统的大概情况,大家觉得怎么样?
http://only-x.com/2013/12/16-rails-permission.html
说下好与不好的地方,或是新的想法,大家一起学习学习
赞一下重复造轮子的精神。
定义了三个 ability: own/read/write
只有这三个 ability,怎么控制更细精度的权限?Linux 系统权限还有 executeable 呢。
Cancan 灵活在可以根据 action 名来做验证。如果能把 Cancan 的权限配置持久化到数据库中,也不错。
@camel linux 也就 own / group + r w x 啊 我这里只搬了一点点,own + r w 没有 group 与 x 当然这里只做了对资源的控制,相对来说比 cancan 精细多了
比如,我文章里说的 cancan 要让用户可以修改自己的东西,必然要写一个类似 record.user_id == current_user.id 来判断用户是否可操作资源
而我写的方式是在创建时就让指定用户可以管理 record,方式不一样,当然优缺点也不一同
如 cancan 想把一个权限控制的代码保存的数据库是非常麻烦的,而且不能修改(相对于普通使用者),而我的方式是不关心记录是谁的,只关心用户对这个资源有没有权限操作,这个确可以随意定义,缺点也很明显,很多权限记录
这只是一时想到的东东,肯定还在很多问题的,不过正好我的项目中在使用,可以慢慢完善 ,这过程中我也同样会遇到很多坑的
#3 楼 @xjz19901211 “定义”需要一个类似"配置"的过程,Rails 鼓励"约定大于配置"。Cancan 直接用 action 名,并支持一些别名,如:read = [:index, :show]
这样挺好。
我一同事一直纠结性能问题,不会每次请求都要带一次权限验证的 sql 查询吧。然后他一直抱怨 cancan 的,不知道楼主有例子分享吗?或者有打算做成 gem?很感兴趣。。
#5 楼 @small_fish__ 性能问题确实是大问题。cancan 每次 action 都会先做判断生成一张权限表然后对照这张表看某个 acion 是否被允许,会造成大量的无用 sql。
#7 楼 @small_fish__ 权限本来就不是一件复杂的事情。如果想方便但不想用 cancan 可以去看看 pundit。自己写的话直接在 app_controller 里 before_filter 一下就可以了。
#6 楼 @liudangyi group 就用Rolify,和 CanCan 搭配很好 https://github.com/EppO/rolify/wiki/Tutorial
#6 楼 @liudangyi CanCan 性能是个头痛的问题,想办法做 cache 吧,把can?
用 cache 包一层,通过判断 user updated_at 来判断是否过期,我还没尝试过。
@camel @small_fish__ 性能问题,我想把权限 cache 的内存中应该还好吧,原谅我一直没做过请求量灰常多的项目。。
这个 cache 可以把用户的权限 cache 一下,或是把所有权限数据也缓存起来。。
@liudangyi 权限是不复杂,不过涉及到各种业务需求时,还是很麻烦的
看下了 Pundit 感觉和 Cancan 差不多,都是制定一个规则,权限基于这个规则 我想试着做下以资源的权限为基础,在这上面随意定制规则
知识面比较窄,说的不对还请见谅
@small_fish__ 我 hack 了 cancan 的内部结构
class DelayBlock
attr_accessor :block, :ability, :args
def initialize(*args, &block)
self.ability = args.shift
self.args = args
self.block = block
end
def run
if @result
@result
else
@result = self.ability.instance_exec(*args, &block)
end
end
end
module CanCan
class Rule
# replace delay hash to normal hash
def transfer_delay_conditions!(ability)
recurse_replace_delay_result(self.conditions, ability)
end
def recurse_replace_delay_result(hash, ability)
hash.each do |k, v|
if v.is_a?(DelayBlock)
hash[k] = v.run
elsif v.is_a?(Hash)
recurse_replace_delay_result(v, ability)
end
end
end
end
module Ability
#override
# used for single match
def can?(action, subject, *extra_args)
match = relevant_rules_for_match(action, subject).detect do |rule|
# ********* add by ankun ***********
rule.transfer_delay_conditions!(self)
# ********* // add by ankun ***********
rule.matches_conditions?(action, subject, extra_args)
end
match ? match.base_behavior : false
end
# override
# accessible_by
def model_adapter(model_class, action)
adapter_class = ModelAdapters::AbstractAdapter.adapter_class(model_class)
# ********* add by ankun ***********
rules = relevant_rules_for_query(action, model_class)
rules.each do |rule|
rule.transfer_delay_conditions! self
end
# ********* // add by ankun ***********
adapter_class.new(model_class, rules)
end
def attributes_for(action, subject)
attributes = {}
relevant_rules(action, subject).map do |rule|
# ********* add by ankun ***********
rule.transfer_delay_conditions!(self)
# ********* // add by ankun ***********
attributes.merge!(rule.attributes_from_conditions) if rule.base_behavior
end
attributes
end
end
end
以后你就可以执行
db = DelayBlock.new do
User.where(:active => true, :admin => false).pluck(:id)
end
can :close, User, :id => db
这样灵活和性能都有所体现,你只要在 DelayBlock 中返回数组就行了。
这样的好处是,只有要调用 can?(:close, @user)
或者 User.accessible_by(current_ability, :close)
的时候才会执行 DelayBlock 中的 sql 查询。希望对你有帮助