casbin的使用及数据库关联Python

Python3.11

Python3.11

Conda
Python

Python 是一种高级、解释型、通用的编程语言,以其简洁易读的语法而闻名,适用于广泛的应用,包括Web开发、数据分析、人工智能和自动化脚本

Casbin 是一个强大的,高效的开源访问控制框架,其权限管理机制支持多种访问控制模型,各个编程语言都对 casbin 有支持

目前我的所有项目,都是基于简单的角色管理来的

分为了三类,未登录能访问的,普通用户能访问的,管理员能访问的

后续做 rbac 系统,那就会把角色和访问控制结合起来,casbin 就可以更好的帮助我去做这个事情

网上关于 casbin 的文档其实还挺多的,但是他们的案例很多都不太适合,新手第一次看很容易看懵

这个文档就结合实际的 web 中的访问控制来进行讲解

基本使用

下载

pip install casbin

casbin 有两个部分组成,一个是配置文件,一个是规则集

初次学习,先把 demo 跑通,先不用搞懂这两个东西是什么

准备两个文件,model.conf 和 policy.csv 文件

model.conf

[request_definition]
r = sub, obj, act
​
[policy_definition]
p = sub, obj, act
​
[matchers]
m = r.sub == p.sub && r.obj == p.obj && r.act == p.act
​
[policy_effect]
e = some(where (p.eft == allow))

policy.csv

p, zhangsan, /index, GET
p, zhangsan, /home, GET
p, zhangsan, /users, GET
p, zhangsan, /users, POST
p, wangwu, /index, GET
p, wangwu, /home, GET

demo

import casbin
import logging
​
def check(e, sub, obj, act):
    ok = e.enforce(sub, obj, act)
    if ok:
        print(f"{sub} CAN {act} {obj}")
    else:
        print(f"{sub} CANNOT {act} {obj}")
​
def main():
    # 初始化日志配置
    logging.basicConfig(level=logging.INFO)
    
    try:
        # 创建Casbin enforcer
        e = casbin.Enforcer("./model.conf", "./policy.csv")
    except Exception as err:
        logging.fatal(f"NewEnforcer failed: {err}")
        return
​
    # 执行权限检查
    check(e, "zhangsan", "/index", "GET")
    check(e, "zhangsan", "/home", "GET")
    check(e, "zhangsan", "/users", "POST")
    check(e, "wangwu", "/users", "POST")
​
if __name__ == "__main__":
    main()

正常情况下,你会得到

zhangsan CAN GET /index
zhangsan CAN GET /home
zhangsan CAN POST /users
wangwu CANNOT POST /users

Casbin 配置文件

最开始看到 model.conf 和 policy.csv 文件,估计还是一脸懵的

那让我们来看看 casbin 的配置文件

Request

代表请求。看我们上面的例子:

[request_definition]
r = sub, obj, act

代表一个请求有三个标准元素,请求主体,请求对象,请求操作

以用户 fengfeng 用 GET 请求 /api/users 接口为例:

请求主体就是 fengfeng

请求对象就是 /api/users

请求操作就是 GET

Policy 和 Policy_Rule

Policy 代表策略,它表示具体的权限定义的规则是什么

在 policy.csv 文件中定义的策略就是 policy_rule。它和 Policy 是一一对应的。

例如我们上面的例子:

[policy_definition]
p = sub, obj, act

我们定义了 policy 的规则如此,那么我们在 policy.csv 中每 ⼀⾏ 定义的 policy_rule 就必须和这个属性 ⼀⼀ 对应

Matcher

有请求,有规则,那么请求是否匹配某个规则,则是 matcher 进行判断的,例如:

[matchers]
m = r.sub == p.sub && r.obj == p.obj && r.act == p.act

表示当

r.sub == p.sub && r.obj == p.obj && r.act == p.act

的时候返回 true,否则返回 false,也就是三者完全匹配的时候,返回 true

Effect

Effect ⽤ 来判断如果 ⼀ 个请求满 ⾜ 了规则,是否需要同意请求。它的规则 ⽐ 较复杂 ⼀ 些。

比如我们这里:

[policy_effect]
e = some(where (p.eft == allow))

这里的 some 表示括号中的表达式个数大于等于 1 就行。 我们这句话的意思就是将 request 和所有 policy 比对完之后,所有 policy 的策略结果(p.eft)为 allow 的个数 >= 1,整个请求的策略就是 true

访问控制模型

除了文档中提到的,casbin 还支持多种访问控制模型

Supported Models | Casbin

ACL 访问控制

ACL 是最早也是最基本的一种访问控制机制,它的原理非常简单:

每一项资源,都配有一个列表,这个列表记录的就是哪些用户可以对这项资源执行 CRUD 中的那些操作。当系统试图访问这项资源时,会首先检查这个列表中是否有关于当前用户的访问权限,从而确定当前用户可否执行相应的操作。总得来说,ACL 是一种面向资源的访问控制模型,它的机制是围绕 资源 展开的。

上面的 demo 就是 acl 访问控制的例子

增加规则

e.AddPolicy("wangwu", "/users", "POST")
e.SavePolicy()

删除规则

e.RemovePolicy("wangwu", "/users", "POST")
e.SavePolicy()

RBAC 访问控制

ACL 模型在用户和资源都比较少的情况下没什么问题,但是用户和资源量一大,ACL 就会变得异常繁琐

想象一下,每次新增一个用户,都要把他需要的权限重新设置一遍是多么地痛苦

RBAC(role-based-access-control)模型通过引入角色(role)这个中间层来解决这个问题

每个用户都属于一个角色,例如开发者、管理员、运维等,每个角色都有其特定的权限,权限的增加和删除都通过角色来进行

这样新增一个用户时,我们只需要给他指派一个角色,他就能拥有该角色的所有权限

修改角色的权限时,属于这个角色的用户权限就会相应的修改

mode.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) && r.obj == p.obj && r.act == p.act

policy.csv

p, admin, /index, GET
p, admin, /home, GET
p, admin, /users, GET
p, admin, /users, POST
p, yunwei, /index, GET
p, yunwei, /home, GET
g, zhangsan, admin
g, wangwu, yunwei

判断的代码还是一样的

check(e, "zhangsan", "/index", "GET")
check(e, "zhangsan", "/home", "GET")
check(e, "zhangsan", "/users", "POST")
check(e, "wangwu", "/users", "POST")

但是有了角色之后,系统新增一个用户,只需要给这个用户分配角色

然后可以在角色管理中新增一个角色,然后设置角色的访问权限,可以访问哪些接口

在这个模式下,一个用户是可以有多个角色的,例如

p, admin, /index, GET
p, admin, /home, GET
p, admin, /users, GET
p, admin, /users, POST
p, yunwei, /index, GET
p, yunwei, /home, GET
g, zhangsan, admin
g, wangwu, yunwei
g, wangwu, admin

增加角色

e.AddPolicy("kuaiji", "/users", "GET")
e.SavePolicy()

增加用户-角色对应关系

e.AddRoleForUser("zhangsan", "kuaiji")
e.SavePolicy()

删除用户-角色对应关系

e.RemoveGroupingPolicy("zhangsan", "kuaiji")
e.SavePolicy()

删除角色

e.RemovePolicy("kuaiji", "/users", "GET")
e.SavePolicy()

结合数据库使用

其实基于上面的rbac,就可以做一个角色访问控制功能了

但是目前整个规则集是存储在文件中的

实际应用中肯定还是会把casbin的规则集放到数据库里面

import casbin
from casbin_sqlalchemy_adapter import Adapter
from sqlalchemy import create_engine
from sqlalchemy.orm import sessionmaker
import logging
from datetime import datetime
​
# 初始化日志配置
logging.basicConfig(
    level=logging.INFO,
    format='%(asctime)s - %(levelname)s - %(message)s',
    datefmt='%Y-%m-%d %H:%M:%S'
)
logger = logging.getLogger(__name__)
​
class RBACManager:
    def __init__(self):
        self.engine = None
        self.session = None
        self.enforcer = None
        self.adapter = None
        self.init_db()
        self.init_casbin()
    
    def init_db(self):
        """初始化数据库连接"""
        try:
            # 创建数据库
            self.engine = create_engine(
                "mysql+pymysql://root:root@127.0.0.1:3306/rule_db?charset=utf8mb4",
                pool_pre_ping=True,
                pool_size=10,
                pool_recycle=3600
            )
            Session = sessionmaker(bind=self.engine)
            self.session = Session()
            logger.info("数据库连接成功")
        except Exception as e:
            logger.fatal(f"数据库连接失败: {str(e)}")
            raise
​
    def init_casbin(self):
        """初始化Casbin权限系统"""
        try:
            # 创建适配器并自动建表
            self.adapter = Adapter(self.engine)
            self.adapter.create_table()  # 自动创建casbin_rule表
            
            # 加载模型文件
            model = casbin.Model.from_file("./testdata/model.pml")
            
            # 创建带缓存的Enforcer
            self.enforcer = casbin.CachedEnforcer(model, self.adapter)
            self.enforcer.set_expire_time(3600)  # 1小时缓存
            self.enforcer.load_policy()
            
            logger.info("Casbin初始化完成")
        except Exception as e:
            logger.fatal(f"Casbin初始化失败: {str(e)}")
            raise
​
    def check_permission(self, sub, obj, act):
        """权限检查方法"""
        ok = self.enforcer.enforce(sub, obj, act)
        status = "允许" if ok else "拒绝"
        logger.info(f"用户 {sub} 请求 {act} {obj} - {status}")
        return ok
​
    def add_permission(self, role, resource, action):
        """添加权限策略"""
        try:
            self.enforcer.add_policy(role, resource, action)
            self.enforcer.save_policy()
            logger.info(f"添加策略: {role} 可 {action} {resource}")
        except Exception as e:
            logger.error(f"添加策略失败: {str(e)}")
​
    def add_role(self, user, role):
        """为用户分配角色"""
        try:
            self.enforcer.add_role_for_user(user, role)
            self.enforcer.save_policy()
            logger.info(f"为用户 {user} 分配角色 {role}")
        except Exception as e:
            logger.error(f"分配角色失败: {str(e)}")
​
    def remove_role(self, user, role):
        """移除用户角色"""
        try:
            self.enforcer.delete_role_for_user(user, role)
            self.enforcer.save_policy()
            logger.info(f"移除用户 {user} 的角色 {role}")
        except Exception as e:
            logger.error(f"移除角色失败: {str(e)}")
​
    def remove_permission(self, role, resource, action):
        """移除权限策略"""
        try:
            self.enforcer.delete_policy(role, resource, action)
            self.enforcer.save_policy()
            logger.info(f"移除策略: {role} 不可 {action} {resource}")
        except Exception as e:
            logger.error(f"移除策略失败: {str(e)}")
​
if __name__ == "__main__":
    try:
        rbac = RBACManager()
        
        # 测试用例
        rbac.add_permission("admin", "/api/users", "GET")
        rbac.add_role("zhangsan", "admin")
        
        # 权限检查
        rbac.check_permission("zhangsan", "/api/users", "GET")  # 应允许
        
        # 移除权限
        rbac.remove_role("zhangsan", "admin")
        rbac.remove_permission("admin", "/api/users", "GET")
        
        # 再次检查
        rbac.check_permission("zhangsan", "/api/users", "GET")  # 应拒绝
        
        # 持久化保存(实际已自动保存)
        rbac.enforcer.save_policy()
        
    except Exception as e:
        logger.error(f"程序运行出错: {str(e)}")

您可能感兴趣的与本文相关的镜像

Python3.11

Python3.11

Conda
Python

Python 是一种高级、解释型、通用的编程语言,以其简洁易读的语法而闻名,适用于广泛的应用,包括Web开发、数据分析、人工智能和自动化脚本

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值