快捷搜索:

一个带完整的RBAC授权系统的rails应用(第一部分

这是个简单的RBAC授权系统。着实说简单也不简单,只不过在誉满举世的rails框架下,统统都是变得分外轻松,当然这还要拜我们一会要用到的两个插件所托。废话少说,让我们说说我们的项目吧。这是一个类似维基的网站,你可以说它是一个简陋版,什么也好,它的开拓代号已被定为Wiki了!项目要求也很简单,用户注册后就可以宣布器械。但这似乎用不了什么授权系统,是以我们还得把它搞繁杂一点点,顺便一提,我们的授权系统是闻名的RBAC(Role Based Access Control,基于角色的造访节制)。在变更多真个授权需求中,我们只需引进一个新的角色就可以摆平它们了。由于角色(Role)在RBAC,是作为必然数量的权限的聚拢的存在。加之,作为迅敏开拓的代名词,rails早已供给了一大年夜打这样的插件,是以我说简单也未尝弗成。

好了,现在从新核阅我们的项目,我们要求用户只有注册登录后才能颁发文章,咳咳,这里,说正规点吧,能颁发词条(lemma)。嗯,这里我们第一个资本lemma有了。当然,假如这些词条一多,就不好办,以是我们要引进标签云这个时髦的器械。为了鼓励用户创建词条或撰写高质量的词条,我们默认每个词条都拥有必然的积分,当用户完成该词条后就获得此积分。有些词条可能不太好写,我们就要相对应前进这些词条的含金量,这个虽然是词条的一个属性,然则我们不能容许用户经由过程编辑该属性来作弊。也为了防止出在荒秽不堪或其他反动违法的器械出在我们的网站,我们很有必须建立类似于BBS的版主轨制,我称之为 peace maker(秩序守护者),当然还要有疏忽统统规则的存在,把不称职的peace maker剔除出治理层,我称之为providence breake(忤逆天意之人)。peace maker可以改变某个词条的积分,但不能为自己或别人加分,加分是由系统自动履行。作为维基,当然少不了共笔系统,我们可以让用户颠末某一步骤成为别人创建的词条的合营创作者,但光是出勤不着力是无法得到积分的,必须进行改动才让加分,并且不让重分加分,是以我们必须在这里做一个开关。无疑,词条与用户是多对多关系,它们之间必要一其中心表,我们在那里做四肢举动便是!嗯,大年夜体便是这样,其他边做边想,rails不必要像java那样一开始就一大年夜打文档滴,这样就不是迅敏开拓了!

我们的开拓对象是netBeans,情况是window XP,ruby 1.8.6,rails 2.3.2,因为我们一会要用到2.3的新特性,以是请务必进级到rails2.3。那么,我们开始吧。

创建一个新项目。

指定命据库。

创建数据库。

接着是安装插件,第一个是闻名的restful-authentication。我们必要用到它的登录验证,作为ruby社区最闻名的认论 railsforum.com中最长的评论争论串所争吵出来的成果,它绝对值得信赖。第二个是declarative authorization,我们的授权系统基础就靠它了。它有什么优点?供给从视图到节制器到模型的授权节制,声明式调用授权规则,让授权逻辑与营业逻辑相分离,集中在一个设置设置设备摆设摆设文件用人道化的DSL编写授权规则,供给图形化界面让你理顺繁杂的授权系统,等等。不过现在让我们打开敕令台,定位到项目的根目录吧:

ruby script/plugin install git://github.com/technoweenie/restful-authentication.git

git clone git://github.com/stffn/declarative_authorization.git vendor/plugins/declarative_authorization

注:这里必要安装Git。自己google吧!

创建User模块与Session模块,为了直奔主题,我们不必要邮箱激活注册,自动登录等时髦的功能,是以根据安装插件时的提示,只要输入:

ruby script/generate authenticated user sessions

rake db:migrate

在RBAC授权模型中,User与Session既是代表两个资本,也是两个紧张元素。在一样平常的授权系统中,User是作为权限的拥用者或主体而存在,在RBAC授权模型中,环境微有改变,正确来说,是权限的附加体或宿主。权限被移到我们一下子提到的Role的身上。Session在RBAC中是对照隐晦的一个元素。不用说,Session首先为我们办理Web无状态的难题,这个事情主要由restful-authentication做了。第二,我们可以用session来承载所有经由过程授权的用户。这个异常紧张,正如前面提到的那样,在RBAC授权模型中,User仅仅是纯挚的用户,权限已被剥离掉落。User是不能与Privilege 直接关联,User 要拥有对某种资本进行操作,必须经由过程Role去关联。假如用户注册了,我们可以分配他一个叫user(注册用户)或wikier(维客)这样的角色,但假如他没有注册或没有登录,岂不是在重重关卡的网站中寸步难行,一个网站总有一些公开资本吧,这岂不是与我们的初衷相悖。是以我们要求只管用户没有登录也要给他一个角色,这个事情由declarative_authorization认真。

打开users_controller与sessions_controller按要求把include AuthenticatedSystem注释掉落。

改动application_controller,此中,current_user,logged_in?都是来自restful- authentication插件;为了安然,我们还用了filter_parameter_logging措施,把像password这样敏感的字段从日志中过滤掉落,以防黑客窃视到!

接着下来这一步异常紧张,我们要向User表添加roles字段,亦即RBAC的Role元素,是权限分配的单位与载体,是用户(Users)与权限(Permissions)的代理层,用来解耦权限和用户的关系。我们也可以把Role自力出来一个模块,让User在同一光阴中拥有多个角色,但现在我们统统从简,以削减数据库的JOIN操作。

ruby script/generate migration AddRolesToUsers roles:text

class AddRolesToUsers"wikier"

end

def self.down

remove_column :users, :roles

end

end

然后:

rake db:migrate

在开始做User模块之前,我们照样做一些锁碎的事情,这会让我们往后做出来的器械看起来赏心悦目。无错,便是筹划工程的全局模板。在app/views/layouts/目录下添加application.html.erb,详细代码为:

它用到许多自定义措施,我们在app/helper目录新建layout_help.rb,把它们放到里面去:

module LayoutHelper

def title(page_title, show_title = true)

@content_for_title = page_title.to_s

@show_title = show_title

end

def show_title?

@show_title

end

def stylesheet(*args)

content_for(:head) { stylesheet_link_tag(*args.map(&:to_s)) }

end

def javascript(*args)

args = args.map { |arg| arg == :defaults ? arg : arg.to_s }

content_for(:head) { javascript_include_tag(*args) }

end

end

改动users_controller,完善其restful功能。此顶用到before_filter,不单单是为了DRY,还斟酌到后面的造访节制。改动后的代码如下:

class UsersController[:show, :edit, :update, :destroy]

before_filter :new_user, :only => :new

def index

@users = User.all

end

def new;end

def create

logout_keeping_session!

@user = User.new(params[:user])

success = @user && @user.save

if success && @user.errors.empty?

self.current_user = @user # !! now logged in

redirect_to users_url

flash[:notice] = "注册成功。"

else

flash[:error]= "注册掉败。"

render :action => 'new'

end

end

def show;end

def edit;end

def update

if @user.update_attributes(params[:user])

flash[:notice] = "更新用户成功。"

redirect_to @user

else

render :action => 'edit'

end

end

def destroy

@user.destroy

flash[:notice] = "删除用户成功。"

redirect_to users_url

end

protected

def load_user

@user = User.find params[:id]

end

def new_user

@user = User.new

end

end

改动_user_bar.html.erb

"log in"%>

"log in"%>

"create an account"%>

添加users#index视图

帐号:

角色:

操作:

'Are you sure?', :method => :delete%>

添加users#show视图

帐号:

邮箱:

角色

删除public的index.html,在routes.rb添加新的路由规则。

ActionController::Routing::Routes.draw do |map|

map.logout '/logout', :controller => 'sessions', :action => 'destroy'

map.login '/login', :controller => 'sessions', :action => 'new'

map.register '/register', :controller => 'users', :action => 'create'

map.signup '/signup', :controller => 'users', :action => 'new'

map.resources :users

map.resource :session

map.root :users

map.connect ':controller/:action/:id'

map.connect ':controller/:action/:id.:format'

end

启动rails:

ruby script/server

改动users#new视图

好了,让我们新建edit.html.erb,处置惩罚roles这个紧张字段吧。User模块平日是利用法度榜样最紧张的地方,编辑删除等操作一样平常只能由治理员来履行,用户就算能抵及这种敏感的地带,也一样平常是一个被阉割了的编辑界面。例如User模型中的login与password用来让用户登录法度榜样,一样平常是不让改动,以是我们在表单中disabled了这个域,密码更是用了SHA1 加密,弗成反向破译,干脆不供给改动。为了保护User模型大年夜部分字段的安然,我们还用了“白名单”;这个restful_authentication插件已经为了我们做了,但roles字段是我们后来添加的,我们得着手改动一下。在User模型的 attr_accessible措施的着末添加上roles就是,即:

attr_accessible :login, :email, :name, :password, :password_confirmation,:roles

再在模型中添加以下代码(超紧张)

def role_symbols

@role_symbols ||= (roles || []).map {|r| r.to_sym}

end

新建users#edit视图

true%>

"请选择",:selected => 0} %>

到此,已经没有restful authentication什么事了,接着下来我们就可以专注于授权系统的开拓。

打开environment.rb,添加:

config.load_once_paths += %W( #{RAILS_ROOT}/lib )

这是用来包管declarative_authorization插件不会加载掉足。

打开application_controller,添加

before_filter :set_current_user

protected

def set_current_user

Authorization.current_user = current_user

end

这个全局的前置过滤器,会把restful authentication的current_user代入到declarative authorization的同名措施current_user中。留意,这两个current_user都是Session工具,里面装载着我们的 User工具,这就包管了我们多个页面中可以保存用户的信息。假如restful authentication的current_user为空,declarative authorization就会创建一个匿名的User工具,把它放进declarative authorization的current_user中。它拥有一个role_symbols属性(这便是为什么我们自定义的User模型要添加一个 role_symbols措施),值为guest。而guest实质是declarative authorization为我们预设的默认角色,当哀乞降任何用户都没有关联或当一个用户没有任何角色时被调用。这样就实现了User与Role的关联了。

匿名User的源码,位于{RAILS_ROOT}/vendor\plugins\declarative_authorization\lib\declarative_authorization\authorization.rb里面

# Represents a pseudo-user to facilitate guest users in applications

class GuestUser

attr_reader :role_symbols

def initialize (roles = [:guest])

@role_symbols = roles

end

end

打开users_controller,开启节制器级其余造访节制。

class UsersController[:show, :edit, :update, :destroy]

before_filter :new_user, :only => :new

filter_access_to :all

filter_access_to [:show, :edit, :update], :attribute_check => true

#………………

end

这里涉及到两个名词:粗粒度与细粒度。

粗粒度:表示种别级,只检核工具的种别(Class),而不穷究其某一个特定的实例。

细粒度:表示实例级,即必要斟酌详细工具的实例(Instance)。比如,只有本人才能查看与编辑自己的用户资料。这个我们经由过程对照ID或某个特定的属性可以实现,是以:arrtibute_check => true(开启属性反省)便是为这筹备的。

更绝的是我们还可以开启模型级其余造访节制,系统就会根据你的权限重写查询指令,防止数据被不法操纵。

require 'digest/sha1'

class User

不过,因为restful authentication插件的缘故原由,当我们登录时,即造访sessions#create action时,restful authentication不只改变了Session工具的状态,还试图改变Session工具里面的User工具的状态,而我们一样平常不容许用户在未登录时改变User工具的状态(这相称于update了!),就会报错。

Authorization::NotAuthorized (No matching rules found for update for # (roles [:guest], privileges [:update, :manage], context :users).):

##它要求我们的guest角色还要拥有update与mange的特权,我们一样平常只让guest在users资本上拥有read_index与create的特权。

##这属性于后面定义授权规则的内容,再往下看几段就明白了!

是以,我们照样放弃对User模型进行模型级其余授权节制吧。

此外,还有视图级其余造访节制,直接封杀页面上的链接。相对应,节制器级其余造访节制是封杀地址栏的url,模型级其余造访节制是来对待黑客的。前两个可能是误操作引起的,着末一种肯定来者不善。视图级其余造访节制一下子再说,由于没有定制好授权规则,很难为大年夜家给出一个直不雅的效果。

为了实现授权规则与营业逻辑相分离,declarative authorization指定把所有的授权规则都定义到一个设置设置设备摆设摆设文件中,并为我们供给专门的DSL来编写授权规则。首先,我们得把\vendor \plugins\declarative_authorization目录下的样本authorization_rules.dist.rb复制到 config目录下,并更名为authorization_rules.rb。打开authorization_rules.rb,去掉落注释,它应该是这个样子的:

authorization do

role :guest do

end

end

privileges do

privilege :manage, :includes => [:create, :read, :update, :delete]

privilege :read, :includes => [:index, :show]

privilege :create, :includes => :new

privilege :update, :includes => :edit

privilege :delete, :includes => :destroy

end

全部代码分为两大年夜块,授权块与特权块。

先看特权块,已给定了五个特权,四个简单的特权(:read,:update,:create,:delete)和一个复合特权 (:manage)。include 后面的便是controller的action的名字。看下图,在rails的利用中,我们要对某个资本进行操作,必须颠末action。而对付某些 action,它因此另一个action为条件。如update action,一定先颠末edit action,经由过程edit action衬着出视图,再有edit.html上的表单提交达到到update action。是以,那四个简单特权就构建index、show、edit、update和destroy这五个restful风格的action的造访节制上就行。假如不知足的话,也可以自定义action,再对其进行造访节制。

您可能还会对下面的文章感兴趣: