策略描述语言(论文)
摘要: 为了保护云资源的安全,防止数据泄露和非授权访问,必须对云平台的资源访问实施访问控制。然而,目前主流云平台通常采用自己的安全策略语言和访问控制机制,从而造成两个问题:
- 云用户若要使用多个云平台,则需要学习不同的策略语言,分别编写安全策略;
- 云服务提供商需要自行设计符合自己平台的安全策略语言及访问控制机制,开发成本较高。
对此,提出一种基于元模型的访问控制策略描述语言PML及其实施机制PML-EM.PML支持表达BLP、RBAC、ABAC等访问控制模型。
PML-EM实现了3个性质:
策略语言无关性
、访问控制模型无关性
和程序设计语言无关性
,从而降低了用户编写策略的成本与云服务提供商开发访问控制机制的成本。
在线论文地址:http://www.jos.org.cn/jos/article/abstract/5624
Casbin框架介绍
Casbin 是一个强大的、高效的开源访问控制框架,支持
Go, Java, Node.js, Javascript (React), Python, PHP, .NET, C++, Rust
等十几种语言。
Casbin 开源项目由北京大学罗杨博士创立于 2017 年,核心维护团队有二十多人。Casbin 在业界具有广泛影响力。
目前已经被 Intel 、VMware 、Orange 、RedHat 、T-Mobile
等公司开源使用,被腾讯云、Cisco 、Verizon
等公司闭源使用。具体详见 Casbin 主页。Casbin Go 主项目目前 GitHub 10000+ stars,加上所有语言的实现、插件等可达到 15000+ stars
。
官方地址:https://casbin.org/
核心技术
基于创始人在北大提出的PERM元模型的学术论文(英文版:arXiv,中文版: 软件学报)
PERM:Policy, Effect, Request, Matcher
Model语法和策略存储
Model语法
- Model CONF 至少应包含四个部分:
[request_definition]、[policy_definition]、[policy_effect]、[matchers]
- 如果 model 使用 RBAC, 还需要添加
[role_definition]
部分。 Model CONF
可以包含注释。注释以#
开头,#
将注释整行。
Model存储
与 policy 不同,model 只能加载,不能保存。因为我们认为 model 不是动态组件,不应该在运行时进行修改,所以我们没有实现一个 API 来将 model 保存到存储中。
Policy存储
在Casbin中,适配器(adapter,Casbin的中间件)
实现了policy规则写入持久层的细节。Casbin的用户可以调用adapter的LoadPolicy()
方法从持久层中加载policy规则,同样也可以调用SavePolicy()
方法将Policy规则保存到持久层中。
支持的 Model
ACL (Access Control List)访问控制列表
具有 超级用户
的 ACL没有用户的 ACL
: 对于没有身份验证或用户登录的系统尤其有用。没有资源的 ACL
: 某些场景可能只针对资源的类型, 而不是单个资源, 诸如 write-article, read-log等权限。 它不控制对特定文章或日志的访问。RBAC (基于角色的访问控制)
支持资源角色的RBAC
: 用户和资源可以同时具有角色 (或组)。支持域/租户的RBAC
: 用户可以为不同的域/租户设置不同的角色集。ABAC (基于属性的访问控制)
: 支持利用resource.Owner这种语法糖获取元素的属性。RESTful
: 支持路径, 如/res/*
,/res/:id
和HTTP
方法,如GET
,POST
,PUT
,DELETE
。拒绝优先
: 支持允许和拒绝授权, 拒绝优先于允许。优先级
: 策略规则按照先后次序确定优先级,类似于防火墙规则。
模型Model元语定义说明
最简单的ACL模型配置
[request_definition]
r = sub, obj, act
[policy_definition]
p = sub, obj, act
[policy_effect]
e = some(where (p.eft == allow))
[matchers]
m = r.sub == p.sub && r.obj == p.obj && r.act == p.act
在线编辑器:https://casbin.org/en/editor
Casbin插件生态
支持多种访问控制模型
适用场景
多租户(tenant)
协同工作
项目实战
项目架构
数据表设计
认证&授权
更多了解:https://cloud.tencent.com/developer/article/1695510
多租户&中间件
多租户数据表设计
注:同一个RDS实例的不同数据库
中间件权限验证
注:需要权限访问模块接口中间件。这里使用不同的Sass站点有不同的数据库。
权限验证类Permission.php
/**
* @desc 权限验证
* @author Tinywan(ShaoBo Wan)
* @date 2020/3/29 10:37
*/
declare(strict_types=1);
namespace app\common\library;
use app\common\service\JwtService;
use Casbin\Exceptions\CasbinException;
use tauthz\facade\Enforcer;
use think\facade\Log;
use think\Request;
class Permission
{
// 不验证权限节点
protected array $public_rule = [
'/f/v1/books',
'/f/v1/homes',
];
/**
* @desc: 权限验证检测
* @return bool
* @author Tinywan(ShaoBo Wan)
*/
public function check(): bool
{
$url = $this->request->baseUrl();
if (in_array($url, $this->public_rule)) {
return true;
}
try {
$uid = JwtService::getCurrentUID();
$action = $this->request->method();
if (!Enforcer::enforce(strval($uid), $url, strtoupper($action))) {
return false;
}
} catch (CasbinException $exception) {
Log::error('Cabin 授权异常' . $exception->getMessage());
return false;
}
return true;
}
}
中间件调用类AuthorizationMiddleware.php
class AuthorizationMiddleware
{
/**
* @param $request
* @param Closure $next
* @return mixed
* @throws AuthorizationException
* @throws Exception
*/
public function handle($request, Closure $next): Response
{
if (!(new Permission($request))->check()) {
throw new AuthorizationException();
}
return $next($request);
}
}
接口访问权限流程
+-----------------------
| RESOURCE EXISTS ? (if private it is often checked AFTER auth check)
+-----------------------
| |
NO | v YES
v +-----------------------
404 | IS LOGGED-IN ? (authenticated, aka has session or JWT cookie)
or +-----------------------
401 | |
403 NO | | YES
3xx v v
401 +-----------------------
(404 no reveal) | CAN ACCESS RESOURCE ? (permission, authorized, ...)
or +-----------------------
redirect | |
to login NO | | YES
| |
v v
403 OK 200, redirect, ...
(or 404: no reveal)
(or 404: resource does not exist if private)
(or 3xx: redirection)
基于角色的RBAC模型(Model)配置
keyMatch2函数:URL 路径或 : 模式下,例如:
/v1/notices/:id
regexMatch函数:任意字符串,正则表达式模式,列如:
GET、get、Get、(GET|POST|DELETE)
实际代码模型配置
rbac-model.conf
[request_definition]
r = sub, obj, act
[policy_definition]
p = sub, obj, act
[role_definition]
g = _, _
[policy_effect]
e = some(where (p.eft == allow))
[matchers]
m = g(r.sub, p.sub) && keyMatch2(r.obj, p.obj) && regexMatch(r.act, p.act) || r.sub == 10086
策略表记录
区县策略:以上针对区县 area 角色,定义了5条策略,分别是通知公告接口的列表、创建、修改、删除、详情。
账号权限:账号 103233、103234、10370488 继承了区县策略组 area,则账号 103233、103234、10370488 将会拥有通知公告接口的列表、创建、修改、删除、详情的访问权限。
小问题:仅只需要给账号 10370488 开通一条特殊的接口权限(如:健康码查询接口),该权限不能添加到策略组 area。那么可行的解决方案有哪些?:
- 第一版方案(低级):入侵,修改代码,在代码层给账号 10370488增加判断放行
- 第二版方案(中级):非入侵,修改代码,通过AOP解决
- 第三版方案(高级):非入侵,不修改代码,策略规则表只需要添加一条以下一条策略
传统框架 VS 现代化框架
原理
在常驻内存框架中使用Casbin。Swoole、Workerman、ReactPHP 运行模式为多进程,而多进程中数据是互相隔离的(每个进程都是独立互不干扰的,这意味着每个进程都维护着自己的资源、变量和类实例等)。注意:内存溢出,垃圾回收机制(GC)。
场景
当Enforcer中的策略发生变化时,调用 Watcher,向消息队列(MQ)中推动消息,监听该消息队列的Enforcer收到后,自动刷新该实例中的策略。
注意:在 PHP-FPM 环境下,并不需要Watcher,因为每个请求都是一个独立的fpm进程,都会实例化一个全新的Enforcer实例。
实现
这里通过基于workerman的PHP异步redis客户端 workerman/redis 的发布订阅模式实现(观察者模式)。
Enforcer & Watcher
实例化Enforcer 同时监控订阅策略的变化,实时刷新到内存中。
更多源码查看地址:https://github.com/php-casbin/webman-permission