python - 求助Tornado/Cyclone RBAC 修饰符编写

 上善若水纯_310 发布于 2022-10-31 19:41

其实我在使用的是Cyclone,构建于Twisted之上,但是API却是使用Tornado的。考虑到国内Python Weber少,Tornado Weber相对多一些,所以做一次标题党。

借鉴Flask经验

无论Cyclone/Tornado都只实现了auth类。但是业务本身的RBAC需要用户自己搞。我参考了O'Reilly Flask Web Development作者Grinberg的做法。

  • 为User表格增加Role,并作为外键;
  • 添加Role表格,其中permission一栏中以二进制表达细颗粒的权限,比如编辑文章为0x01,删除文章代表0x02等......添加人员,删除人员等,基本上这个二进制整数会随着开发变得很长。
  • 在Controller(他们称之为Views)入口处判断,读取用户的Role表,再读取permission表,再与当前所需要的权限比较后,转向403或者继续执行渲染。

但是就RBAC的逻辑来说,不嫌麻烦的话,可以在每个Controller入口处判断一下。

简陋而繁琐

给大家看看我的代码(没有code标签,所以代码格式破了):

pythonclass DoctorHandler(BaseHandler):
    @cyclone.web.authenticated
    @storage.DatabaseSafe
    @defer.inlineCallbacks
    def get(self):
        user = yield storage.users.find_first(
            where=("user_email=%s", self.current_user["email"]))

        if not user:
            self.clear_current_user()
            self.redirect("/")
            defer.returnValue(None)

        role = yield storage.roles.find_first(
            where=("id=%s", user.user_role))

        if not role:
            self.clear_current_user()
            self.redirect("/")
            defer.returnValue(None)

        if role.name not in "Doctor":
            m = "Unauthorized Access"
            raise cyclone.web.HTTPError("403", m)

        m = "Hello, %s, You can do %s"%(role.name, role.permission)

        self.render("dashboard.html", role=m)

Flask 代码中的修饰符

我预计该项目应该有100个URL Controller入口,所以最好以decorator形式减少冗余代码。我们看看Grinberg的代码。

pythonclass User(UserMixin, db.Model):
    def can(self, permission):
        return self.role is not None and (self.role.permissions & permissions) == permissions

    def is_admin(self):
        return self.can(Permission.ADMINISTRATER)

......
decorator.py

pythonclass permission_required(permission)
    def decorator(f):
        @wraps(f)
        def decorated_function(*args, **kwargs):
            if not current_user.can(permission):
                abort(403)
                return f(*args, **kwargs)
            return decorated_function
        return decorator

    def admin_required(f):
        return permission_required(Permision.ADMINISTRATER)(f)         

所以,我原来的代码可以写成:

pythonClass SomeControllerHandler(BaseHandler):
    @cyclone.web.authenticated
    @storage.DatabaseSafe
    @defer.inlineCallbacks
    @permission_required(Permission.MODERATE_COMMENTS)
    def get():
        print "Only moderator is allowed"  

    @admin_required
        print "only admin is allowed"

Flask与Cylone/Torndao的不同点

理想状态下,所有RBAC判断应该以decorator形式存在。但是Tornado/Cyclone是异步框架,而且decorator本身就是需要异步访问数据库。而异步访问本身就是decorator,也就是说需要用decorator来修改decorator。比如storage.DatabaseSafe,defer.inlineCallbacks...

......增加了调试难度。

其实,Flask在app/models中user模型是完备的。而Tornado/Cyclone演示程序中的app/storage中user类居然是空白的。所有的动作居然都是在Views.py即控制器中去做的。所以说,Tornado/Cyclone并没有严格按照MVC的模型在做。所以代码移植起来有难度。

在Flask的decorator中需要访问user.can方法,而Tornado/Cyclone的current_user居然是保存在Cookie中的。两者完全不同。

目标
我的目标就是修正Cyclone/Tornado的用户App的设计,将decorator引入到RBAC的设计中。否则,太繁琐,难以维护了。

希望有过这方面经验的人给一些建议。

1 个回答
  • 自己来回答。

    这个问题没有简单答案,也是Python的难点之一。需要先阅读Python语言中相关内容,包括:

    • 高阶函数

    • 装饰符与装饰器模式

    • functools

    • 异步I/O

    阅读逻辑是:

    • AOP使用到装饰器实现代码简化的目的;

    • 异步I/O使用装饰器,配合yield实现异步I/O或协程;

    • 装饰器的原始形式是高阶函数

    • Python使用装饰符@实现装饰器

    • Python装饰器可以嵌套

    2022-10-31 23:10 回答
撰写答案
今天,你开发时遇到什么问题呢?
立即提问
热门标签
PHP1.CN | 中国最专业的PHP中文社区 | PNG素材下载 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有