-
Notifications
You must be signed in to change notification settings - Fork 114
Tutorial.Authentication Management I.zh_cn
- 引言
权限控制在软件系统里非常重要,它能防止一些重要的信息透露给非相关的用户或管理员,从而保证系统的安全。Pi Engine也为开发者和用户提供了权限开发与操作,对于管理员,可以通过后台配置为不同角色的管理员分配相应的权限,对于开发者,Pi Engine提供了Pi\Acl\Acl类来进行权限功能的开发。
1.1 Pi权限机制简介
在Pi Engine中,权限是作为一个资源,即Acl Resource,当初始化系统时,这个资源将会被载入,用于用户权限判定的方法checkAccess()会被附加到dispatch事件驱动里。同时这个方法的优先级也被设置为最高,这能够保证在访问到页面之前先判定用户的权限。
图8-1 载入Acl资源源码
而在checkAccess()方法里,Pi Engine会去检查请求的用户对当前页面是否有访问权限,若没有则会去跳转到禁止访问的流程。因此在Pi Engine后台配置权限时,Pi Engine可以决定用户能否看到相关的系统或模块页面(Controller & Action),但对于模块内容某一资源的权限,后台可以配置,但Pi Engine无法决定是否显示这个资源,这需要开发者对模块进行相应开发。
Pi Engine提供了几个数据表用于保存角色、资源以及角色和资源的关系数据,这些数据表不需要开发者直接操作,Pi Engine封装了Pi\Acl\Acl类来操作这些数据表,而在checkAccess()方法里,也是通过Acl实例来判断用户权限。关于更多的Acl类的使用可参考Pi Engine文档。
1.2 角色、资源及权限
在使用权限时会涉及到这几个概念:角色、资源以及角色与资源间的对应关系,即权限。这里介绍下Pi Engine里这几个概念的定义。
角色也就是一个帐号在系统里的身份,目前Pi Engine在前后的角色是独立的,即一个帐号在前后台分别对应了一个角色,这两个角色之间没有联系。Pi Engine在前后台都定义了一些默认角色,如前台有:
- member
- guest
- pending
- banned
后台为:
- staff
- moderator
- editor
- manager
Pi Engine支持角色的单继承和多继承,如在后台角色中,moderator继承自staff、manager继承自moderator和editor。而模块也可以定义角色,这些角色也是公有角色。
资源可理解为用户能获取到的任何东西,大到一个页面,小到一个按钮或者是一个文字。因此资源可以是一个页面,一般用Controller & Action来定义,或者是用一个字符串描述来指代一个资源,比如我们用publish-article-edit来指代页面上的edit按钮,角色与publish-article-edit对应的关系为denied时,说明不允许该用户操作此按钮。
权限就是定义角色允许或禁止访问某一资源。在Pi Engine里默认是角色禁止访问所有模块资源,如果需要更改,可用超级管理员帐号去后台修改。
在Pi Engine中,父类角色的allow权限无条件往后派生。即父类若对一个资源是允许的,不管子类如何修改权限,它对该资源都是允许的。这可以用下图来示意:
图8-2 Pi中对Allow权限的继承
- 创建角色、资源及权限
上面的介绍里,我们已经提到过,角色、资源等数据都保存在数据表里,并且在模块里也可以定义这些信息,这部分我们将介绍如何创建角色、资源以及关联角色和资源。同时也将介绍如何将一个资源定义为页面资源或字符串资源。
2.1 创建角色
模块里角色的创建是通过配置完成的,这个配置就是acl.php,在安装的时候角色信息将被写入到core_acl_role表里。下面代码介绍了如何创建角色并指定义角色间的继承关系:
<?php
return array(
'roles' => array(
// Case 1
'frontRole1' => array(
'title' => __('Front role'),
),
// Case 2
'childRole1' => array(
'title' => __('Role have one parent'),
'section' => 'admin',
'parents' => array('editor'),
),
// Case 3
'parent1' => array(
'title' => __('Parent1'),
'section' => 'admin',
'parents' => array('staff'),
),
'parent2' => array(
'title' => __('Parent2'),
'section' => 'admin',
),
'childRole2' => array(
'title' => __('Role have two parent'),
'section' => 'admin',
'parents' => array('parent1', 'parent2'),
),
),
);
角色的定义在role.php配置里以roles字段标识,roles数组里的每一个子数组就是一个角色。这里需要注意的是,这里子数组的键值不仅在该模块里唯一,还不能与已经安装的模块角色的name相同。在roles数组里我们采用了三个例子说明角色的定义。
在例子里,frontRole1, childRole1, parent1等就是角色唯一名称。titile字段就是模块名,用于显示,在例子中我们写成了描述的形式,实际情况下最好用比较简短的单个词来表示。例子创建了一个前台角色,这个角色无父类。section字段用来表示角色为前台或后台角色,front为前台,admin为后台,如果没有标志默认为前台,如Case 1所示。
Case 2定义了一个后台角色,这个后台角色继承自系统后台的editor角色,角色的继承用parents字段来标识,父类角色的name以数组的形式赋给该字段。
Case 3定义了两个后台角色,其中一个角色继承自系统角色staff,另一个无父角色,接着又定义了一个子角色继承自这两个角色,即多继承,多个角色也同样以数组的形式赋给parents字段。在这个例子中,childRole2间接地继承自staff角色。
2.2 创建资源
资源的创建同样也是要acl.php配置里完成的,而资源是以resources字段来标识。而资源间也允许继承,下面的代码介绍了如何定义一个资源以及资源间的继承:
<?php
return array(
'roles' => array(
...
),
'resources' => array(
// Case 1
'front' => array(
'module-resource1' => array(
'module' => 'demo',
'title' => __('Resource1'),
),
),
// Case 2
'admin' => array(
'module-resource2' => array(
'module' => 'demo',
'title' => __('Resource2'),
),
'module-resource3' => array(
'module' => 'demo',
'title' => __('Resource3'),
'parent' => 'module-resource2',
),
),
),
);
在resource字段里,分别用front和admin来区分该资源为前台或后台资源。front和admin数组里的每一个子数组就是一个资源。其中module-resource1, module-resource2就是资源名,这个资源名在整个系统里也是唯一的。在每个资源数组里,module字段表示该资源所属的模块;title为资源名,用于显示;parent定义了该资源的父资源。Case 2就定义了一个资源module-resource2以及其子资源module-resource3。
上述代码里定义的资源都是字符串资源,如果要定义一个页面资源,需要添加一个page.php配置,并关联Controller & Action和acl.php里的资源。page.php另外的功能是用来定义某一个Controller & Action的缓存、是否支持组装,这部分可以参考Pi Engine的文档。在page.php配置里添加权限相关的代码如下:
<?php
return array(
'front' => array(
),
'admin' => array(
// Case 1
array(
'controller' => 'index',
'permission' => array(
'parent' => 'module-resource2',
),
),
// Case 2
array(
'title' => __('Page 1'),
'module' => 'demo',
'controller' => 'test1',
'permission' => array(
'access' => array(
'staff' => 1,
'parent1' => 0,
),
),
),
),
// Case 3
'exception' => array(
array(
'controller' => 'ajax',
),
),
);
在page.php里同样以front和admin来区分前后台的页面,每一个子数组就是一个页面,以Controller/Action来表示。例子1中,我们定义了一个Controller资源,同时用permission字段来标识角色对这个资源的权限,这里用parent字段将这个资源继承自acl.php配置里定义的字符串资源module-resource2,这样这个资源将与module-resource2资源拥有相同的权限。因此以后就可以用module-resource2来作为这个controller资源的资源名。
例子2也定义了一个页面资源及其权限(权限将在下一节讲),所不同的是,这里直接在permission数组里定义了权限,也就是这个controller资源没有与acl.php配置里的字符串资源关联,因此这个资源的资源名就是这种形式:
array(
'module' => 'demo',
'controller' => 'test',
);
在很多时候,我们在后台会有一些AJAX调用,这些调用我们希望默认就能访问,例子3就给出解决方法,用exception字段告诉系统这个controller或action不需要走权限判定流程。这样AjaxController在任何情况下都可以被访问。
2.3 创建权限
权限的定义也同样在acl.php配置里完成,用户对资源的访问权限主要以access字段来定义:
<?php
return array(
...
'resources' => array(
...
'admin' => array(
'module-resource2' => array(
'module' => 'demo',
'title' => __('Resource2'),
'access' => array(
'staff' => 1,
'parent1' => 0,
),
),
...
),
),
);
上述代码为module-resource2定义了角色的访问权限,在module-resource2资源里以access字段标识,access数组里,键值为角色唯一名称,值为0或1,为0时说明不允许该角色访问,为1时为允许,若不标识,默认为0。
- 禁止导航资源时,隐藏导航
在后台Operation页里,一个导航项(如List页)会对应一个页面(如IndexController/listAction),这个页面也是一个资源(如资源名为list-items),当我们禁止某角色访问该资源时,当然也希望该角色在Operation页里看不到List页这个导航项,因此就需要在导航配置navigation.php里作些修改:
return array(
'admin' => array(
'pagea' => array(
...
'controller' => 'index',
'action' => 'list',
'resource' => array(
'resource' => 'list-items',
),
),
...
),
...
);
代码里用resource字段来标识,resource数组里也用resource字段标识资源名,list-items就是在acl配置里定义的资源名。
**注意:**这里定义了两个配置文件acl.php和page.php,由于page.php里引用到acl.php,因此安装时需要先安装acl.php配置,即在module.php里,acl.php的引用要在page.php之前。