途牛原创|大话权限中心的PHP架构之道

权限管理是无线运营系统中的核心模块,通过访问控制策略的配置,来约定人与资源的访问关系。

本文着重讲解如何通过PHP来构建一个灵活、通用、安全的权限管理系统。

关于权限

首先我们来聊聊权限。

权限系统一直以来是我们应用系统不可缺少的一个部分,若每个应用系统都重新对系统的权限进行设计,以满足不同系统用户的需求,将会浪费我们不少宝贵时间,所以花时间来设计一个相对通用的权限系统是很有意义的。

系统目标:对应用系统的所有对象资源和数据资源进行权限控制,比如 应用系统的功能菜单、各个界面的按钮、数据显示的列以及各种行级数据 进行权限的操控。

权限模型

设计初期,我们学习了Amazon的 IAM ,经过对比分析,最终我们选用 RBAC3模型 来指导系统的设计工作。

RBAC认为权限授权实际上是Who、What、How的问题。在RBAC模型中,who、what、how构成了访问权限三元组,也就是“Who对What(Which)进行How的操作”。

  1. Who:权限的拥用者或主体(如Principal、User、Group、Role、Actor等等)

  2. What:权限针对的对象或资源(Resource、Class)。

  3. How:具体的权限(Privilege,正向授权与负向授权)。

  4. Operator:操作。表明对What的How操作。也就是Privilege+Resource

  5. Role:角色,一定数量的权限的集合。权限分配的单位与载体,目的是隔离User与Privilege的逻辑关系.

PHP架构之道

「如何用PHP构建我们的权限中心」

接下来我们将从 编码规范、依赖管理、数据源架构、数据处理、单元测试 等方面来体验一把PHP的神奇之旅。

编码规范

好的编码规范可以改善软件的可读性,可以促进团队成长,可以减少Bug,可以降低维护成本,可以。。。(这么X,我们必须要推广)

PHP社区一直百花齐放,拥有大量的函数库、框架和组件,因而PHP代码遵循或尽量接近同一个代码风格就非常重要。

框架互操作组(即PHP标准组)发布了一系列推荐风格。

  1. 阅读PSR-0

  2. 阅读PSR-1

  3. 阅读PSR-2 Coding Style Guide

  4. 阅读PSR-4 Autoloader

权限中心的目录结构:

-- /tuniu/rbac
|-- src
| |-- App //应用建模层
| | |-- City.php
| | |-- Cms.php
| | |-- Menu.php
| |-- App.php
| |-- Auth.php
| |-- Orm //ActiveRecord层
| | |-- App
| | | |-- Resource
| | | | |-- Map.php
| | | |-- Resource.php
| | | |-- User.php
| | |-- App.php
| | |-- Log.php
| | |-- Role
| | | |-- User.php
| | |-- Role.php
| | |-- Rule.php
| | |-- User.php
| |-- Orm.php
| |-- Utils.php
|-- tests //测试
| |-- bootstrap.php
| |-- fixtures
| | |-- null.yml
| | |-- rbac
| | | |-- app.yml
| | | |-- auth.yml
| | | |-- role.yml
| |-- rbac
| | |-- appTest.php
| | |-- authTest.php
| | |-- roleTest.php
|-- vendor
|-- composer.json
|-- composer.lock
|-- phpunit.xml
|-- README.md

PSR-2,权限应用资源类:

namespace Tuniu\Rbac\Orm\App;

use Tuniu\Rbac\Orm;
use Tuniu\Rbac\Orm\App;
use Tuniu\Rbac\Orm\App\Resource\Map;
use Tuniu\Rbac\Orm\Rule;
use Tuniu\Rbac\Orm\User;

class Resource extends Orm {}

PSR-4,命名空间的约定:

()*\

类名
文件路径

\Tuniu\Rbac\Orm\App\Resource
/tuniu/rbac/src/Orm/App/Resource.php

\Tuniu\Rbac\App\Cms
/tuniu/rbac/src/App/Cms.php

依赖管理

Composer 是PHP中用来管理依赖(dependency)关系的工具。你可以在自己的项目中声明所依赖的外部工具库(libraries),Composer会帮你安装这些依赖的库文件。

权限中心的依赖声明:

{
"name": "tuniu/rbac",
"require": { //声明依赖关系
"php": ">=5.3.0",
"squizlabs/php_codesniffer": "2.*", //PHP_CodeSniffer检查代码规范
"php-activerecord/php-activerecord": "dev-master" //ActiveRecord
},
"require-dev": { //声明开发依赖
"phpunit/phpunit": "~4.6",
"phpunit/dbunit": ">=1.2"
},
"autoload": {
"psr-4": {
"Tuniu\Rbac\": "src/" //命名空间
}
}
}

检查代码规范,执行单元测试。

$./vendor/bin/phpcs --config-set default_standard PSR2
$./vendor/bin/phpcs src
$./vendor/bin/phunit

^^^^^^ 眼涩,眼酸,眼疲劳,怎么办。。。

骚年,如果舒服了,就使劲往下滑动吧。。。

争渡,争渡,惊起一滩鸥鹭。

数据源架构

基于权限中心各表的关系(各种关联,各种回调),采用传统的模式,必将把精力耗在无尽的循环中。

于是乎,开始寻觅一种数据源的架构模式,Active Record很靠谱的出现了。

Active Record(中文名:活动记录)是一种领域模型模式,特点是一个模型类对应关系型数据库中的一个表,而模型类的一个实例对应表中的一行记录。

PHP ActiveRecord 是一个基于ActiveRecord设计模式开发的开源PHP/ORM库。它旨在大大简化与数据库的交互和减少手写SQL语句。它不同于其他的ORM,你不需要使用任何的代码生成器,也不费劲去手写、维护模型层的表映射文件。这个库的灵感来自Ruby on Rails,因此它也借鉴Ruby on Rails的想法和实现。(谁用谁知道)

下面介绍下这个小伙伴给编程带来的快乐:

  1. Validation(数据验证)

validates_presence_of

  1. validates_inclusion_of

场景(角色表数据约定)

/

  • 1:约定角色类型的范围
  • 2:约定角色状态的范围
    */
    public static $validates_inclusion_of = array(
    array('f_type', 'in' => array('role', 'group', 'department', 'member')),
    array('f_status', 'in' => array(1,2))
    );

/

  • 设定角色名称、角色状态、角色类型、角色描述不能为空
    */
    public static $validates_presence_of = array(
    array('f_name'),
    array('f_status'),
    array('f_type'),
    array('f_desc')
    );

//录入数据不满足约定条件,就无法保存,再也不用担心那些脏脏的数据。

  1. Callback(回调)

before_save

  1. before_create

  2. before_update

  3. before_destroy

  4. after_save

  5. after_create

  6. after_update

  7. after_destroy

场景1(角色表操作记录)

//定义回调函数
public static $before_save = array('setMisc');

//每当角色表保存之前,都默默的把数据格式好,好开心。。。
public function setMisc()
{
//创建时间,创建人
$this->is_new_record() && ($this->f_create_at = date("Y-m-d H:i:s"));
$this->is_new_record() && ($this->f_create_by = $this->op);
//更新时间,更新人
$this->f_update_by = $this->op; //操作人
$this->f_update_at = date("Y-m-d H:i:s"); //操作时间
}

场景2(新增资源,推送资源映射表):

//定义回调函数
public static $after_save = array('syncRelations');

//每当资源保存之后,自动把资源数据中的映射字段值,推送给资源映射表。
public function syncRelations()
{
//获取资源的映射字段
$mapFiledValue = $this->getMapFiledValue();

$mapFiledValue    ? $this->addMap($mapFiledValue)    : Map::removeAllByResourceId($this->id);

}

场景3(角色删除):

//定义回调函数
public static $after_destroy = array('deleteRelations');

//每当角色删除时,自动把系统中角色的成员和角色的资源规则清空,而且是事务的。
public function deleteRelations()
{
UserRelations::removeAllByRoleId($this->id);
RuleRelatinos::removeAllByRoleId($this->id);
}

  1. Association(关联)

has_many

场景(新增角色用户)

//定义角色表和角色用户表的关系
public static $has_many = array(
array(
'relations',
'foreign_key' => 'f_role_id',
'class_name' => "\Tuniu\Rbac\Orm\Role\User",
)
);

//新增角色用户,默默的把role_id传递给了角色用户表,此处如果用SQL,简直不忍直视。
Role::first()->create_relation(
array(
'f_user_id' => $uid
)
);

  1. 事务(一致性与安全性)

权限系统中数据一致性和数据安全性的重要性是不言而喻,不用事务会被BS的。

在此我们郑重承诺,权限系统中每一次数据增删改请求,都是事务处理的。

比如角色保存:

self::transaction(
function () use ($params, &$role) {

    $role->f_name   = $params['name'];    $role->f_status = $params['status'];    $role->f_type   = $params['type'];    $role->f_desc   = $params['desc'];    if ($role->is_invalid()) {        throw new \Exception('角色相关操作失败', '900202');    }    $role->save();}

);

篇幅有限,这个小伙伴的战斗力场景远甚于此。

坦白的说,用AR是一种编程享受。

单元测试(持续交付)

一切都如此的完美,没有测试,又如何可以证明这件事情的完美,又如何可以保障交付的质量。

PHPUnit 是一个轻量级的PHP测试框架。它是在PHP5下面对JUnit3系列版本的完整移植,是xUnit测试框架家族的一员(它们都基于模式先锋Kent Beck的设计)。

单元测试是一种提高软件质量非常有效的方法,但很重要的是我们要去实践和体会。

简单的介绍下权限管理中的角色行为测试用例:

角色行为测试

  1. 数据集(YAML)

t_rbac_user:

  • f_id: 1
    f_name: "zhaoyang2"
    f_create_at: "2013-10-10 17:04:05"
    t_rbac_role_user:
  • f_id: 1
    f_user_id: 1
    f_role_id: 1
    t_rbac_role:

  • f_id: 1
    f_name: "运营研发部-1"
    f_status: 1,
    f_type: "department"
    f_desc: "我们是运营研发部-1"
    f_create_at: "2013-10-10 17:04:05"
    f_create_by: "zhaoyang2"
    f_update_by: "zhaoyang2"

  1. 测试代码:

//预设数据集
public function getDataSet()
{
return new \PHPUnit_Extensions_Database_DataSet_YamlDataSet(
fixture('rbac/role.yml')
);
}

/

  • 权限操作-异常验证
  • @expectedException Exception
  • @expectedExceptionMessage 您无权运营当前数据
    */
    public function testRoleDeleteException()
    {
    Role::first()->remove();
    }

/

  • 权限操作-删除验证
    */
    public function testRoleDelete()
    {
    Role::first()->remove("zhaoyang2");

    //验证数据行
    $this->assertEquals(1, $this->getConnection()->getRowCount(Role::$table_name));
    $this->assertEquals(1, $this->getConnection()->getRowCount(UserRelation::$table_name));
    }

鉴权用例和应用管理用例,远比这个复杂,感兴趣的同学可以去 Fork 一把,了解下PHPUNIT的魅力。

PHPUNIT很强大,想合理运用的话,没有任何捷径,开始写测试用例吧。。

结束语

其实说架构算上下,就是和大家分享下权限中心的PHP之道。

高效便捷的使用PHP服务我们的工作。

多交流,多分享,书写更好的PHP代码,享受编程和技术所带来的快乐。

  1. RBAC

  2. PHP之道

  3. 深入理解PHP内核

关键字:php, phpunit, psr, composer

版权声明

本文来自互联网用户投稿,文章观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处。如若内容有涉嫌抄袭侵权/违法违规/事实不符,请点击 举报 进行投诉反馈!

立即
投稿

微信公众账号

微信扫一扫加关注

返回
顶部