`

使用Rails做一个RBAC的权限管理系统(不使用插件)

阅读更多
前几天,稍微分析了一下RBAC形式的权限管理系统的实现原理,然后我使用Rails做了一个。

先来说一下表间的关系:
users <--> roles roles <--> permissions
很简单,用户和角色是多对多关系,角色和权限也是多对多关系,那么关于权限管理这一块就一共有5张表。

具体的表结构:
还是直接看数据库定义文件吧!
ActiveRecord::Schema.define(:version => 20080708074044) do

  create_table "permissions", :force => true do |t|
    t.string   "name"
    t.string   "action"
    t.datetime "created_at"
    t.datetime "updated_at"
  end

  create_table "permissions_roles", :id => false, :force => true do |t|
    t.integer  "permission_id", :limit => 11
    t.integer  "role_id",       :limit => 11
    t.datetime "created_at"
    t.datetime "updated_at"
  end

  create_table "posts", :force => true do |t|
    t.string   "title"
    t.text     "body"
    t.datetime "created_at"
    t.datetime "updated_at"
  end

  create_table "roles", :force => true do |t|
    t.string   "name"
    t.datetime "created_at"
    t.datetime "updated_at"
  end

  create_table "roles_users", :id => false, :force => true do |t|
    t.integer  "role_id",    :limit => 11
    t.integer  "user_id",    :limit => 11
    t.datetime "created_at"
    t.datetime "updated_at"
  end

  create_table "users", :force => true do |t|
    t.string   "name"
    t.string   "password"
    t.string   "email"
    t.datetime "created_at"
    t.datetime "updated_at"
  end

end


在User这个Model中有以下两个关键的方法:
#Get user's all permissions
	def permissions
		user_permissions = Array.new
		for role in self.roles
			user_permissions += role.permissions			
		end
		user_permissions.uniq!
		user_permissions
	end
	
	#Get user's allowed requests uri
	def permission_urls
		user_urls = Array.new
		user_permissions = Array.new
		for role in self.roles
			user_permissions += role.permissions			
		end
		for user_permission in user_permissions
			user_urls << user_permission.action			
		end
		user_urls
	end


现在,大致的意思,相信应该说清楚了吧,我对于用户权限的验证就是根据url,准确地说,应该是根据controller名称和action名称来验证的。

那么现在来看看此权限管理系统是如何工作的吧!

在用户登录时,我把用户的id存到session范围中,然后在需要进行权限保护的控制器中加入如下的过滤器:
比如我在后台管理对文章进行管理(Admin::PostController),代码如下:
before_filter :authorize, :only => [:index, :show, :new, :edit, :destroy]
	
	def authorize		
		unless User.find(session[:user_id]).permission_urls.include?( self.class.controller_path + "/" + self.action_name )	
				flash[:notice] = "You have not permission to do it!"
				redirect_to :controller => "admin/welcome"
		end
	end



以上是对于每次请求的权限控制,那么在页面上如何根据登录用户的权限显示相应的权限内容呢?
在后台管理的模块页面中,加入如下的判断语句即可。
<% if User.find(session[:user_id]).allowed_to?("admin/posts/index")%>
		<li id="posts_item"><%= link_to "Post Manage", admin_posts_path %></li>
	<% end %>


以上就是我做这个权限管理系统的基本思想,代码量比较少,思路也蛮请楚的,比较适合小型的项目。

不过我分析了一下这个权限管理系统,它就基于控制器和控制器方法的名称来判断的,而且应该是对使用generate scaffold形式生成的控制器能比较好地起作用。那么这就决定了它有多方面的不足。
1- 如何保护后台管理中的静态页面。
2- 使用不方便,在对permissions进行管理时,要求管理员比较对所用的程序比较熟悉,了解那些controller和action才行(我正在考虑怎么样让程序自动获取后台中的所有控制器和相关方法,并且显示它们的作用)
3- 权限重复比较多。
4- 与系统结合得感觉还是比较紧,我正在考虑如何运行ruby的特性将其变成一个插件(我的目标)

我正在考虑如何改进这些问题,从而做了一个轻量级的好用的权限管理系统。
分享到:
评论
12 楼 kkito 2008-08-01  
年初的时候我用了一个插件,感觉跟楼主这个差不多
activerbac
自己也思考了一下

至于那个控制粒度问题

你怎么控制粒度精确到对资源的操作?
如A建立的POSTS,B能看不能删,C是版主,能看有能删

我觉得也可以实现
应该除destory方法外还有一个destory_by_self方法
一般用户有destory_by_self的权限,而版主有destory权限
或者destory方法只能删除自己的文章,另一个admin_destory可以随便删
就能做到自己的能够删,不能删别人,版主都能删文章的功能
本来逻辑上就可以看成两码事
11 楼 lurena 2008-08-01  
这个基本没有实用性, 实现的权限管理要比这复杂得多.
做教科书还可以.
10 楼 rainchen 2008-08-01  
qichunren 写道
引用
你怎么控制粒度精确到对资源的操作?
如A建立的POSTS,B能看不能删,C是版主,能看有能删


不知道你说的是不是:
你是说对于某个特定的用户创建的资源的权限控制吗?我做的这个不能达到这个要求.

如果只有让B能看不能删posts,让C能看又能删posts,那可以通过我的这个控制.

不知道对于你说的针对某个特定的资源(在我我先暂且理解为table中的一条记录)是如何控制每个用户对其的权限控制,有什么好的方法没有??


我选用 RoleRequirement 这个插件,因为他支持eval一些自定义方法来进行细粒度的判断

class Admin::Listings < ApplicationController
  require_role "contractor"
  require_role "admin", :for => :destroy # don't allow contractors to destroy

  # leverage ruby to prevent contractors from updating listings they don't have access to.
  require_role "admin", :for => :update, [b]:unless => "current_user.authorized_for_listing?(params[:id]) "[/b]  ...
end
"




经过修改,扩展出这种形式的调用:
@user.can_{action}_{controller}?
@user.can_{action}_{controller}?(@target)

以便在View能做更细致的布局

详细可参考以前写的测试例子:
http://www.iteye.com/post/409317
9 楼 reeze 2008-07-31  
现在也很头疼这个问题。。要这针对某个资源进行访问控制。普通的基于权限的方式不是记恨有效。比如一个群组,可以设置访问权限,然后管理员 ,组成员,非组员的控制很不DRY,也很分散。。不知道有没有比较灵活的解决办法~
8 楼 universac 2008-07-29  
rainchen 写道
你怎么控制粒度精确到对资源的操作?
如A建立的POSTS,B能看不能删,C是版主,能看有能删


我现在头疼的也是这个,不光光基于角色,想把对资源的访问也统一控制,一直没有办法,现在都是分散在各个地方。不知道有谁有这方面的经验
7 楼 qichunren 2008-07-29  
引用
你怎么控制粒度精确到对资源的操作?
如A建立的POSTS,B能看不能删,C是版主,能看有能删


不知道你说的是不是:
你是说对于某个特定的用户创建的资源的权限控制吗?我做的这个不能达到这个要求.

如果只有让B能看不能删posts,让C能看又能删posts,那可以通过我的这个控制.

不知道对于你说的针对某个特定的资源(在我我先暂且理解为table中的一条记录)是如何控制每个用户对其的权限控制,有什么好的方法没有??
6 楼 rainchen 2008-07-17  
你怎么控制粒度精确到对资源的操作?
如A建立的POSTS,B能看不能删,C是版主,能看有能删
5 楼 phoenix520 2008-07-17  
qichunren 写道
phoenix520 写道
这个是用户角色资源的3层结构?我们现在使用的是用户角色权限资源的4层结构,但很多时候觉得多了那么一层,哪种结构更加合理一点呢?


说来听听呢!具体的是什么样子的呢?有什么优点?

例如人力资源部这个角色可以看到人力资源部的相关资源,财务部的角色可以看到财务部的相关资源,总经理工作部这个角色可以同时看到所有部门的资源,如果用3层结构,角色直接关联资源,那么关联总经理工作部资源的时候就会把人资部和财务部的资源再关联给总经部,会产生一些重复工作,当然也可以给总经部用户授予人资部和财务部的两个角色,不过这样并不是很直观,如果采用4层结构,财务部角色对应财务部权限,再对应财务部资源,总经部角色对应财务部权限加人资部权限,再对应相关资源,这样子可能会减少些赋权的工作量。
4 楼 qichunren 2008-07-16  
phoenix520 写道
这个是用户角色资源的3层结构?我们现在使用的是用户角色权限资源的4层结构,但很多时候觉得多了那么一层,哪种结构更加合理一点呢?


说来听听呢!具体的是什么样子的呢?有什么优点?
3 楼 superxielei 2008-07-16  
我现在使用的是三层的,并没有权限这一层,这个应该是不同的项目有不同的需求吧。
应该是四层的更完善一点,但应该是可以配置的。
2 楼 phoenix520 2008-07-16  
这个是用户角色资源的3层结构?我们现在使用的是用户角色权限资源的4层结构,但很多时候觉得多了那么一层,哪种结构更加合理一点呢?
1 楼 superxielei 2008-07-16  
说错了。还是删了吧!!

相关推荐

Global site tag (gtag.js) - Google Analytics