PyCasbin 是一个用 Python 语言打造的轻量级开源访问控制框架( https://github.com/casbin/pycasbin ),目前在 GitHub 开源。PyCasbin 采用了元模型的设计思想,支持多种经典的访问控制方案,如基于角色的访问控制 RBAC、基于属性的访问控制 ABAC 等。
1.支持自定义请求的格式,默认的请求格式为{subject, object, action};
2.具有访问控制模型 model 和策略 policy 两个核心概念;
3.支持 RBAC 中的多层角色继承,不止主体可以有角色,资源也可以具有角色;
4.支持超级用户,如 root 或 Administrator,超级用户可以不受授权策略的约束访问任意资源;
5.支持多种内置的操作符,如 keyMatch,方便对路径式的资源进行管理,如 /foo/bar 可以映射到 /foo*
1.身份认证 authentication (即验证用户的用户名、密码),PyCasbin 只负责访问控制。应该有其他专门的组件负责身份认证,然后由 PyCasbin 进行访问控制,二者是相互配合的关系;
2.管理用户列表或角色列表。PyCasbin 认为由项目自身来管理用户、角色列表更为合适,PyCasbin 假设所有策略和请求中出现的用户、角色、资源都是合法有效的。
pip install casbin
1.初始化一个 enforcer,传入两个参数:模型文件路径和策略文件路径;
import casbin
e = casbin.Enforcer("path/to/model.conf", "path/to/policy.csv")
2.在你的代码需要进行访问控制的位置,加入如下钩子;
sub = "alice" # the user that wants to access a resource.
obj = "data1" # the resource that is going to be accessed.
act = "read" # the operation that the user performs on the resource.
if e.enforce(sub, obj, act):
# permit alice to read data1
pass
else:
# deny the request, show an error
pass
3.采用管理 API 进行权限的管理,如获取一个用户所有的角色;
roles = e.get_roles("alice")
PyCasbin 目前正在积极向社区进行推送,并且可以通过插件的方式已经支持与 Django 等 Web 框架进行集成,将来会推广到更多 Web 框架以及社区。Casbin 已经有 Golang 版本、Java 版本、PHP 版本、Node.js 版本、Pytho n版本 等主流语言版本。有跨语言需求的开发者可以只用 Casbin 这一套框架就实现多个不同语言的项目的权限管理任务。
PyCasbin 采用 Apache 2.0 开源协议发布。
前言
公司的软件管理一直没有权限管理模块,最近老板让研究一下Casbin 这个开源库,花了两天简单的了解了一下,由于Casbin没有c/c++版本实现,而我会一点python 语法,所以选择研究一下python 版本pyCasbin的使用
PyCasbin 是一个用 Python 语言打造的轻量级开源访问控制框架目前GitHub(https://github.com/casbin/pycasbin)开源。PyCasbin 采用了元模型的设计思想,支持多种经典的访问控制方案,如基于角色的访问控制 RBAC、基于属性的访问控制 ABAC 等。
主要特性:
1.支持自定义请求的格式,默认的请求格式为{subject, object, action};
2.具有访问控制模型 model 和策略 policy 两个核心概念;
3.支持 RBAC 中的多层角色继承,不止主体可以有角色,资源也可以具有角色;
4.支持超级用户,如 root 或 Administrator,超级用户可以不受授权策略的约束访问任意资源;
5.支持多种内置的操作符,如 keyMatch,方便对路径式的资源进行管理,如 /foo/bar 可以映射到 /foo*
1.安装和使用测试
英文ok 可以官网https://python.ctolib.com/casbin-pycasbin.html 了解一下,或者找个博客https://blog.csdn.net/byywcsnd/article/details/86644190了解一下,安装使用测试还是比较简单的,熟悉python的同学按照文章中的步骤做就行了
2.关于持久化
按照上面链接的文章使用会发现如果调用pycasbin 接口增加删除policy然后保存policy(有自动保存选项),下次再次调用,之前增加或者删除的policy效果无效,查看原来导入 policy对应的文件内容没有变化,查看官方文档发现原来为了保持casbin库的轻量级和灵活性,持久化(保存功能)只提供了虚接口,具体实现需要安装或者自己实现对应的adapter
3.adapter的作用和实现方法
adapter的最主要作用
1.提供把文件或者数据库中的policy信息读取解析导入casbin 中的policy的方法
2.提供把casbin中的policy 存入文件或者数据库中的方法
所以自己实现的adapter只要实现有这两个功能的就行了
class MAdapter(persist.Adapter): #定义一个python 类 MAdapter继承 persist.Adapter这个类,我们看着这个类
`class Adapter:
"""the interface for Casbin adapters."""
def load_policy(self, model):
"""loads all policy rules from the storage."""
pass
def save_policy(self, model):
"""saves all policy rules to the storage."""
pass
def add_policy(self, sec, ptype, rule):
"""adds a policy rule to the storage."""
pass
def remove_policy(self, sec, ptype, rule):
"""removes a policy rule from the storage."""
pass`
可以看出这个基类有方法load_policy,和save_policy,对应上面这个adapter最主要的两个作用,我们按照要求重写这两个方法就行了
由于我的python语法和一些常见的python方法使用不熟练所以做了一个简单的基于json文件的adapter(demon级别只少没有错误异常检测 当policy信息量大的时候保存性能没有考虑),就当抛转作用,大家可以简单参考下写出基于其它的各种文件类型和数据库类型的adapter
4.代码实现和测试
自己写的adapter文件mJsonAdapter.py
import casbin
import json
import os
from casbin import persist
class CasbinRule(object):
#PType=''
#v0=''
#v1=''
#v2=''
#v3=''
#v4=''
#v5=''
__tablename__ = "casbin_rule"
def __init__(self,PType_='',v0_='',
v1_='',v2_='',v3_='',
v4_='',v5_=''):
self.PType=PType_
self.v0=v0_
self.v1=v1_
self.v2=v2_
self.v3=v3_
self.v4=v4_
self.v5=v5_
def load(self):
text = self.PType
if self.v0!='':
text = text+', '+self.v0
if self.v1!='':
text = text+', '+self.v1
if self.v2!='':
text = text+', '+self.v2
if self.v3!='':
text = text+', '+self.v3
if self.v4!='':
text = text+', '+self.v4
if self.v5!='':
text = text+', '+self.v5
return text
def __repr__(self):
return '
class MAdapter(persist.Adapter):
pfpath='none.json'
storeJs=json.loads("{}")
def __init__(self, policyFilePath):
self.pfpath=policyFilePath
def load_policy(self, model):
fd=open(self.pfpath,mode='r')
str=fd.read()
fd.close()
#print (str)
self.storeJs=json.loads(str)
casbinRuleStrct = []
for item in self.storeJs:
cr=CasbinRule(item['PType'],
item['V0'],item['V1'],
'','',
'', '')
if item.get('V2'):
cr.v2=item['V2']
if item.get('V3'):
cr.v2=item['V3']
if item.get('V4'):
cr.v2=item['V4']
if item.get('V5'):
cr.v2 = item['V5']
print (cr.load())
persist.load_policy_line(cr.load(),model)
def _save_policy_line(self, ptype, rule):
csbr = CasbinRule()
csbr.PType=ptype
if len(rule) > 0:
csbr.v0 = rule[0]
if len(rule) > 1:
csbr.v1 = rule[1]
if len(rule) > 2:
csbr.v2 = rule[2]
if len(rule) > 3:
csbr.v3 = rule[3]
if len(rule) > 4:
csbr.v4 = rule[4]
if len(rule) > 5:
csbr.v5 = rule[5]
self.saveCasbinRule(csbr)
def saveCasbinRule(self,casbinRule):
if(casbinRule.PType==''):
return False
js=json.loads("{}")
js["PType"]=casbinRule.PType
if(casbinRule.v0!=''):
js['V0']=casbinRule.v0
if(casbinRule.v1!=''):
js['V1']=casbinRule.v1
if(casbinRule.v2!=''):
js['V2']=casbinRule.v2
if(casbinRule.v3!=''):
js['V3']=casbinRule.v3
if(casbinRule.v4!=''):
js['V4']=casbinRule.v4
if(casbinRule.v5!=''):
js['V5']=casbinRule.v5
flag = 0
for item in self.storeJs:
if (item==js):
flag=1
break
if (flag==0):
self.storeJs.append(js)
def save_policy(self, model):
'''
implementing add Interface for casbin \n
save the policy in mongodb \n
'''
for sec in ["p", "g"]:
if sec not in model.model.keys():
continue
for ptype, ast in model.model[sec].items():
for rule in ast.policy:
self._save_policy_line(ptype, rule)
fd = open(self.pfpath, mode='w+')
fd.write(json.dumps(self.storeJs))
print (json.dumps(self.storeJs))
fd.close()
return True
def add_policy(self, sec, ptype, rule):
"""add policy rules to mongodb"""
self._save_policy_line(ptype, rule)
def remove_policy(self, sec, ptype, rule):
"""delete policy rules from mongodb"""
pass
def remove_filtered_policy(self, sec, ptype, field_index, *field_values):
"""
delete policy rules for matching filters from mongodb
"""
pass
model配置文件 rbac_model.conf
1
[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) && r.obj == p.obj && r.act == p.act
policy导入和保存的文件 rbac_policy.json
[{"PType": "p", "V0": "alice", "V1": "data1", "V2": "read"},
{"PType": "p", "V0": "bob", "V1": "data2", "V2": "write"},
{"PType": "p", "V0": "data2_admin", "V1": "data2", "V2": "read"},
{"PType": "p", "V0": "data2_admin", "V1": "data2", "V2": "write"},
{"PType": "g", "V0": "alice", "V1": "data2_admin"},
{"PType": "p", "V0": "alice", "V1": "data2", "V2": "write"}]
测试代码文件 jsonAdapterTest.py
import casbin
import mJsonAdapter
from mJsonAdapter import MAdapter
adapter=MAdapter('rbac_policy.json')
e=casbin.Enforcer('rbac_model.conf',adapter,True)
f=e.enforce('ywh','data','read')
e.add_policy('ywh','data','read') #'ywh','data','write'
e.save_policy()
if f:
print ("true")
else:
print ("false")
测试方法很简单,因为配置文件中刚开始没有 ‘ywh’,‘data’,'read’这个policy信息,第一次执行打印是false,关闭程序再次执行会发现打印true,同时会发现文件rbac_policy.json内容改变了.
已经通过本人测试,基于json文件adapter 已经简单实现,这只是一个简单版本,提供一下casbin的adapter的实现思路
[参考](https://python.ctolib.com/casbin-pycasbin.html)
[参考](https://casbin.org/docs/en/adapters)
[参考](https://blog.csdn.net/byywcsnd/article/details/86644190)