第八章迭代用户访问控制.docx
《第八章迭代用户访问控制.docx》由会员分享,可在线阅读,更多相关《第八章迭代用户访问控制.docx(40页珍藏版)》请在冰豆网上搜索。
第八章迭代用户访问控制
第八章:
迭代:
用户访问控制
————————————————————————————————作者:
————————————————————————————————日期:
第八章:
迭代5:
用户访问控制
像我们前面制作的TrackStar应用程序这样基于用户的web应用程序中,通常需要针对谁对某一功能提出访问请求进行访问控制。
我们所说的用户访问控制是指在一个较高的层次上,当访问请求被提出时一些需要被思考的问题,例如:
▪请求的提出者是谁?
▪提出请求的用户是否有足够的权限访问该功能?
上述问题的答案可以帮助应用程序做出适当的响应。
在上次迭代中完成的工作使得应用程序有能力提出第一个问题。
我们的基础用户管理应用扩展了应用程序的用户认证流程,使之可以使用数据库内的资料。
应用程序现在允许用户建立自己的认证信息,并且在用户登录时使用储存在数据库内的信息进行匹配。
在用户成功登录后,应用程序就知道接下来的一系列请求是谁提出的。
本次迭代的核心任务是帮助应用程序回答第二个问题。
一旦用户提供了足够的认证信息后,应用程序需要找到一种合适的方法去判断用户是否有足够的权限去执行要求的操作。
我们将利用Yii的用户访问控制中的一些特性来扩展我们的基本授权模型。
Yii即提供了一个简单的访问控制过滤器,也提供了一个更先进的RBAC(基于角色的访问控制)体系,来帮助我们完成授权需求。
我们将在TrackStar应用程序中用户访问需求的实现中仔细观察这2点。
迭代计划
当我们在第3章第一次介绍我们的TrackStar应用程序时,我们曾提及,这个应用程序拥有2个高级别用户类型:
匿名用户和认证用户。
这是一个小小的区别基于成功登录的用户和未登录的用户。
我们也介绍过用户在同一个Project中扮演不同角色的想法。
针对一个Project我们建立了如下的三类角色模型:
▪ProjectOwner(被赋予所有权限访问此Project的管理功能)
▪ProjectMember(被赋予一定权限访问此Project的特性和功能)
▪ProjectReader(只有读取Project相关内容的权限,没有任何修改级权限)
这次迭代的重点是实施方法来管理这个应用程序中用户的访问控制权限。
我们需要一个方法来建立我们的角色和权限,并且分发给用户,同时制定我们期望对每个角色施加的访问控制规则。
为了这个目标,我们需要标明所有我们将在本次迭代中完成的项目细节。
下面的列表是这样一个项目列表:
▪制定一个策略来强制用户在获得访问任何Project或Issue相关功能前,必须登录
▪建立用户角色并且使之与一个特殊的功能权限对应
▪制定一个为用户分配角色的机制(包含角色相关的权限)
▪确保我们的角色权限结构针对每一个Project独立存在(也就是说,允许用户在不同的项目拥有不同的权限)
▪制定一个让用户和项目,同时也和项目中的角色相关联的机制
▪实施适当的认证访问检测,使得应用程序可以针对基于不同用户权限进行允许或拒绝访问操作
幸运的是,Yii内部有大量内部功能可以帮助我们完成这一需求。
所以,让我们大干一场吧。
运行已存在的测试套件
如常,是时候运动一下了,我们需要运行一次所有已存在的单元测试,来确保测试可以通过:
SHELL代码或屏幕回显:
%cdWebRoot/protected/tests/
%phpunitunit/
PHPUnit3.4.12bySebastianBergmann.
................
Time:
3seconds
OK(10tests,27assertions)
一切都没有问题,所以我们可以开始本次旅程了。
访问控制过滤器
我们曾经在第三次迭代的时候介绍过过滤器,当时我们使用它来鉴别项目上下文关系,当处理Issue相关CRUD操作时。
Yii框架提供的过滤器被叫做accessControl(访问控制)。
这个过滤器可以被直接使用在控制器类中,提供一种授权方案来验证用户是否可以使用一个特定的控制器下的行为。
实际上,细心的读者应该能想起,我们在第六章使用filterProjectContext过滤器时,我们曾发现过,访问控制过滤器当时已经存在于IssueController和ProjectController类的过滤器列表中,像下面的样子:
PHP代码:
/**
*@returnarrayactionfilters
*/
publicfunctionfilters(){
returnarray(
'accessControl',
//performaccesscontrolforCRUDoperations
);
}
上面的代码包含在由Gii代码生成器在生成Issue和ProjectAR类的脚手架CRUD操作时,自动生成的代码里的。
默认的实现方法是设置为允许任何人观看一个已经存在Issue和Project项目的列表。
然后,它只允许认证用户进行新建和更新操作,进一步限制帐号为admin的用户享有删除行为。
你也许还记得当我们第一次对Project实施CRUD操作时,我们必须登录才可以执行操作。
同样的情形发生在对Issues和Users的操作上。
这种机械式的授权和访问模式就是由过滤器accessControl实现的。
让我们仔细观察一下在ProjectController.php文件中的这种实现方式。
有2个相关访问控制实现方法在这个文件中,ProjectController:
filters()和ProjectController:
:
accessRules()。
第一个方法的代码如下:
PHP代码:
/**
*@returnarrayactionfilters
*/
publicfunctionfilters()
{
returnarray(
'accessControl',//performaccesscontrolforCRUDoperations
);
}
下面是第二个方法的代码:
PHP代码:
/**
*Specifiestheaccesscontrolrules.
*Thismethodisusedbythe'accessControl'filter.
*@returnarrayaccesscontrolrules
*/
publicfunctionaccessRules()
{
returnarray(
array('allow',//allowalluserstoperform'index'and'view'actions
'actions'=>array('index','view'),
'users'=>array('*'),
),
array('allow',//allowauthenticatedusertoperform'create'and'update'actions
'actions'=>array('create','update'),
'users'=>array('@'),
),
array('allow',//allowadminusertoperform'admin'and'delete'actions
'actions'=>array('admin','delete'),
'users'=>array('admin'),
),
array('deny',//denyallusers
'users'=>array('*'),
),
);
}
filters()方法对我们来说已经非常熟悉了。
我们在这里申明所有将在这个控制器类里使用的过滤器。
在上面的代码中,我们只有一个accessControl,引用了一个由Yii框架提供的过滤器。
这个过滤器调用定义了如何驱动相关访问限制规则的accessRules()方法。
在前面提到的accessRules()方法中,有4条规则被申明。
每一条都以数组形式存在。
该数组的第一个元素是allow(通过)或deny(拒绝)。
分别标识了获得或拒绝相关访问。
剩下的部分由name=>value键值对组成,申明了规则里剩余的参数。
让我们观察一下前面定义的第一条规则:
PHP代码:
array('allow',//allowalluserstoperform'index'and'view'actions
'actions'=>array('index','view'),
'users'=>array('*'),
),
这条规则允许任何用户执行控制器的index和viewactions(行为)。
星号‘*’特殊字符泛指所有用户(包括匿名,已认证,和其他类型)。
第二条规则被定义成如下形式:
PHP代码:
array('allow',//allowauthenticatedusertoperform'create'and'update'actions
'actions'=>array('create','update'),
'users'=>array('@'),
),
这条规则允许认证用户(已登录)执行控制器的create和updateactions。
‘@’特殊字符泛指所有已认证用户。
下面是第三条规则:
PHP代码:
array('allow',//allowadminusertoperform'admin'and'delete'actions
'actions'=>array('admin','delete'),
'users'=>array('admin'),
),
这里申明一个用户名为admin的特殊用户,被允许执行控制器的actionAdmin()和actionDelete()actions。
第四条规则如下定义:
PHP代码:
array('deny',//denyallusers
'users'=>array('*'),
),
它拒绝了所有用户执行所有控制器的的所有action。
定义访问规则的时候可以使用一些contextparameters(内容参数)。
前面提到的规则定义了行为和用户来组成规则的内容,下面是一个完整的参数列表:
▪Controllers(控制器):
这条规则指定了一个包含多个控制器ID的数组,来指明哪些规则需要被应用。
▪Roles(角色):
这条规则指定了一个将被规则使用的授权列表(包括角色,操作,权限等)。
这些是为RBAC的一些功能服务的,我们将在下一个部分进行讨论。
▪Ips(IP地址):
这条规则指定了一组可以被施加到规则的客户端IP地址。
▪Verbs(提交类型):
这条规则制定了可以被施加到规则的HTTP请求类型。
▪Expression(表达式):
这个规则指定了一个PHP表达式,这个表达式的值被用来决定这个规则是否应该被使用。
▪Actions(行为):
这个规则指定了需要被规则匹配的对应actionID(行为ID)的方法。
▪Users(用户):
这个规则指定了应该被施加规则的用户。
登录当前项目的用户名作为被匹配项。
3个特殊字符可以被使用:
o*:
任何用户
o?
:
匿名用户
o@:
登录用户/认证用户
访问规则按照其被申明的顺序一条一条的被评估。
第一条规则与当前模型进行匹配,来判断授权结果。
如果当前规则是一个allow的,这个行为(action)可以被执行;如果是一个deny规则,这个action无法被执行;如果没有规则和内容匹配,这个action仍然可以被执行。
这是第四条被定义的原因。
如果我们不在我们的认证列表末端定义一条deny全部用户全部action的规则,我们就无法完成预期的访问控制。
拿第二个规则来举例,所有通过认证的用户可以执行create和updateactions。
但是它并不会拒绝匿名用户的请求。
它对匿名用户熟视无睹。
这里第四条规则确保所有和上面3条不匹配的请求都被拒绝掉。
当这些都完成后,修改我们的应用程序来拒绝匿名用户访问所有的Project,Issue和user相关的功能,绝对小菜一叠。
我们需要做的是将用户数组的特殊字符’*’替换为’@’。
这将只允许认证用户访问对应控制器的actionIndex()和actionView()方法。
所有其他actions都拒绝认证用户访问。
对我们的控制器进行如下修改。
打开下面3个文件:
ProjectController.php,IssueController.php和userController.php,并且如下修改第一条访问控制规则:
PHP代码:
array('allow',//allowonlyauthenticateduserstoperform'index'and'view'actions
'actions'=>array('index','view'),
'users'=>array('@'),
),
完成这些修改后,应用程序会在访问Project,Issue或User的任何功能前要求登录。
我们依然允许匿名用户访问SiteController类的action方法,这是因为我们的登录方法存在于这个类中。
我们必须允许匿名用户访问登录页。
基于角色的访问控制
现在我们已经使用了一个简单的访问控制过滤器(accessControl)来broadstroke(泛泛的)限制授权用户访问,我们需要关注我们应用程序所需的更细节化的访问控制。
如前所述,用户将在项目中扮演一定的角色。
项目中将出现owner(主管)这样的用户类型,可以被视为项目管理员。
他们将被赋予所有的项目管理权限。
也会有member(成员)这样的用户类型,他们被赋予一部分项目功能权限,为主管权限的一个子集。
最后还有一个reader(读者)用户类型,他们只有读没有任何修改的权限。
为了完成这样的基于一个用户角色的访问模型,我们将探讨Yii的RBAC功能。
在管理已认证用户的访问许可的计算系统安全方面,RBAC是一条已经制定的标准。
简单来说,RBAC标准中定义了一个应用程序中的角色。
执行某些操作的权限也被定义,然后与角色相关联。
然后用户将被分配到一个角色,并且通过这个角色获得与之相关的权限。
对于有需要的读者,有大量关于RBAC概念和标准方面的文档可以阅读。
其中一个优秀资源就是Wikipedia:
http:
//en.wikipedia.org/wiki/Role-based_access_control。
我们将专注与Yii的RBAC应用。
Yii中的RBAC应用是,简单、优雅、和强大的。
Yii中RBAC的基础是一个叫授权项目的idea(想法)。
授权项目就是一系列在应用程序中允许去做的事。
这些允许可以被归类为roles(角色),tasks(任务),或者operations(操作),因此形成权限层级。
角色可有任务(或其他角色)组成,任务可以由操作(或其他任务组成),并且操作是最低权限级别。
例如,在我们的TrackStar应用程序中,我们需要一个owner的角色类型。
所以我们建立一个角色类型名为owner的授权项目。
这个角色将由类似usermanagement(用户管理)和issuemanagement(事务管理)的任务组成。
这些任务进一步包含所需的原子操作。
例如,用户管理任务可以由新建用户,编辑用户和删除用户等操作组成。
权限等级是允许被继承的,来看个例子,如果一个用户被赋予owner角色,他就继承了新建,编辑,删除的操作权限。
一般来说,你赋予一个用户一定数量的角色,这用户就继承了属于这些角色的权限。
这也是Yii中RBAC的真谛。
当然,在这个列子中,我们可以将用户和任何授权项目想连接,而不是一类角色。
这将允许我们灵活的将权限赋予任何级别的用户。
如果我们只想赋予删除用户操作给一个特殊用户,而不是全部owner(主管)权限,我们可以将用户简单的与原子操作相连,这使得Yii中的RBAC相当灵活。
配置认证管理器
在我们建立授权等级,授予用户角色和访问权限检测之前,我们需要我们需要配置授权管理应用组建,authManager。
这个组建用来存储权限信息,和管理权限与所提供的检测用户是否可以执行相关操作的方法之间的关系。
Yii提供了2类授权管理器:
CPhpAuthManager和CDbAuthManager.CphpAuthManager使用PHP脚本文件储存授权信息。
CDbAuthManager,正如你所猜的,使用数据库储存授权信息。
authManager被配置为应用程序组建。
只需要指定使用了2类的中哪一类,并且设置相关初始化类参数,就可完成授权管理器配置。
因为我们已经在TrackStar应用程序中使用了数据库,这使得,选择CDbAuthManager应用是更合理的。
配置方法:
打开位于protected/config/main.php的主配置文件,将下列代码添加至应用组建数组:
PHP代码:
'authManager'=>array(
'class'=>'CDbAuthManager',
'connectionID'=>'db',
),
这里建立了一个名为authManager的新应用程序组建,指定‘class’类型为CDbAuthManager,并且设置了类属性‘connectionID’为我们的数据库连接组建。
至此,我们可以在整个应用程序的任何位置通过Yii:
:
app()->authManager访问它。
建立RBAC使用的数据库表
如前所述,CDbAuthManager类使用数据库表来存储授权信息。
它需要一个特定结构。
结构文件位置:
YiiRoot/framework/web/auth/schema.sql。
这一简单而优雅的结构由3个表组成,AuthItem,AuthItemChild,和AuthAssignment。
表AuthItem用来储存授权项目的定义信息,包括是一个角色,任务或操作。
表AuthItemChild用来储存形成授权项目层次的父子关系。
最后,AuthAssignment表是用来存储用户和授权项目之间关系的关系表。
基本的数据库定义语句如下:
SQL代码:
createtableAuthItem
(
namevarchar(64)notnull,
typeintegernotnull,
descriptiontext,
bizruletext,
datatext,
primarykey(name)
);
createtableAuthItemChild
(
parentvarchar(64)notnull,
childvarchar(64)notnull,
primarykey(parent,child),
foreignkey(parent)referencesAuthItem(name)ondeletecascadeonupdatecascade,
foreignkey(child)referencesAuthItem(name)ondeletecascadeonupdatecascade
);
createtableAuthAssignment
(
itemnamevarchar(64)notnull,
useridvarchar(64)notnull,
bizruletext,
datatext,
primarykey(itemname,userid),
foreignkey(itemname)referencesAuthItem(name)ondeletecascadeonupdatecascade
);
这个结构是直接从Yii框架文件/framework/web/auth/schema.sql中获取的,并未遵从我们的表命名规则。
这些表名是CDbAuthManager类默认定义的。
当然你可以自定义这些表名。
简单起见,我们未做修改。
建立RBAC授权体系
将上述表添加到我们的_dev和_test数据库之后,我们需要将我们的角色和权限添加进去。
我们将使用authManager提供的API来完成此操作。
我们不会在此建立任何RBAC任务。
下图展示了我们希望定义的基本等级关系:
图中的等级关系自上而下展示。
所以Owner享有所有列表中的权限,包括从Member和Reader二个角色中继承来的。
同样的Member继承了Reader的权限。
现在我们需要做的是在应用程序中建立这一等级关系。
入前面提到的,最佳方法是写代码调用authManagerAPI。
举个例子,下面的代码新建了一个角色和一个新操作,然后在角色和权限之间建立关系:
PHP代码:
$auth=Yii:
:
app()->authManager;
$role=$auth->createRole('owner');
$auth->createOperation('createProject','createanewproject');
$role->addChild('createProject');
在上面的代码中,我们首先创建了一个类authManager的实例,然后我们使用createRole(),createOperation,和addChild()这些API接口方法来创建一个新owner角色和一个叫做createProject的新操作。
然后我们为owner角色添加权限。
这个创建示例只是我们需要的等级关系的一小部分,所有之前被列出的都应该以相似的方式创建。
为了完成构建我们所需的权限等级,我们要书写一段在命令行执行的简单的shell命令。
这将是对我们用来创建最初应用程序的命令行工具yiic的一个扩展。
编写控制台应用程序命令
在第二章我们建立HelloWorld程序和第四章构建TrackStar应用程序骨架时我们介绍过yiic命令行工具。
yiic是yii中以命令行形式执行任务的控制台应用程序。
我们曾使用webapp命令来新建应用程序,并且在第二章我们还使用过yiicshell命令来新建一个controller(控制器)类。
我们也曾用新的Gii代码生成工具来新建模型类和CRUD脚手架代码。
这些工作也可以用yiic来完成。
作为一个提示,yiicshell命令允许你通过命令行和web应用程序交互。
你可以从包含应用程序脚本的文件包来执行它。
同时,内部的一些特殊代码,提供了自动生成controllers(控制器),views(视图)anddatamodels(数据模型)的功能.
Yii中的控制台应用程序可以轻松的通过编写自定义命令来扩充,而这正是我们要做的事。
我们将通过编写新命令行工具来扩充yiicshell命令行工具集,这些新命令行工具允许我们使用一致和可重复的方式来构造我们的RBAC授权等级。
为控制台程序编写新命令是非常容易的。
它是继承自CConsoleCommand类,以最低标准继承了所需的在命令被调用时执行的run()方法。
类的名字应该与所期望的命令名字相同,并且在后面加上Command。
在我们的案例中,命令的名字将简单的称为rbac,所以类的名字