前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >Flask 表结构 用户权限划分

Flask 表结构 用户权限划分

作者头像
Python之道
发布2020-06-02 15:15:38
1.2K0
发布2020-06-02 15:15:38
举报
文章被收录于专栏:程序员八阿哥

Flask 用户权限划分

最近学习了下用户权限划分的数据库结构,并且结合到了 Flask 和 SQLAlchemy 中

  • [基础表]
    • [用户表]
    • [角色表]
    • [权限表]
    • [菜单表]
  • [关联表]
    • [用户角色表]
    • [角色权限表]
    • [角色菜单表]
  • [SQLAlchemy]
  • [与 Flask 结合]

首先是数据库的整体结构图(简化版)

权限

基础表#### 用户表

代码语言:javascript
复制
class UserModel(db.Model):
    __tablename__ = 'user'
    username = db.Column(db.String(50))
    password = db.Column(db.String(128))
    email = db.Column(db.String(128))
    mobile = db.Column(db.String(11))
    name = db.Column(db.String(50))
    gender = db.Column(db.SmallInteger)  # 0 未知, 1 男 2 女
角色表
代码语言:javascript
复制
class RoleModel(db.Model):
    __tablename__ = 'role'
    name = db.Column(db.String(20))
权限表
代码语言:javascript
复制
class PermissionModel(db.Model):
    __tablename__ = 'permission'
    name = db.Column(db.String(50))
    action = db.Column(db.String(250), unique=True)
菜单表
代码语言:javascript
复制
class MenuModel(db.Model):
    __tablename__ = 'menu'
    id = db.Column(db.Integer, primary_key=True)
    name = db.Column(db.String(50))
    icon = db.Column(db.String(50))
    url = db.Column(db.String(250))
    order = db.Column(db.SmallInteger, default=0)
    bg_color = db.Column(db.String(50))

关联表

基础表完了就是关联表了

用户角色表

用户跟角色,肯定是多对多的关系,按照 Flask-SQLAlchemy 里的 Many-to-Many Relationships

代码语言:javascript
复制
user_role = db.Table('user_role',  # 用户角色关联表
    db.Column('user_id', db.Integer, db.ForeignKey('user.id')),
    db.Column('role_id', db.Integer, db.ForeignKey('role.id')),
    db.Column('created_at', db.DateTime, default=datetime.now),
)
角色权限表

这里把权限挂在了角色下面,其实也可以去掉角色,直接跟用户挂钩,但是如果后期在后台分配用户权限,估计会累死。这里角色跟权限也是多对多

代码语言:javascript
复制
role_permission = db.Table('role_permission',  # 角色权限关联表
    db.Column('permission_id', db.Integer, db.ForeignKey('permission.id')),
    db.Column('role_id', db.Integer, db.ForeignKey('role.id')),
    db.Column('created_at', db.DateTime, default=datetime.now),
)
角色菜单表

同上,也是多对多

代码语言:javascript
复制
role_menu = db.Table('role_menu',  # 用户菜单关联表
    db.Column('role_id', db.Integer, db.ForeignKey('role.id')),
    db.Column('menu_id', db.Integer, db.ForeignKey('menu.id')),
    db.Column('created_at', db.DateTime, default=datetime.now),
    db.Column('is_delete', db.Boolean, default=False),
)

SQLAlchemy

如果需要获取一个用户的角色,可以利用relationship,关联到角色表上

代码语言:javascript
复制
class UserModel(db.Model):
    # ...
    roles = db.relationship(
        'RoleModel',
        secondary=user_role,
        backref=db.backref(
            'users',
            lazy='dynamic'
        )
    )

获取用户的所有权限可以用property

代码语言:javascript
复制
class UserModel(db.Model):
    # ...
    @property
    def permissions(self):
        permissions = PermissionModel.query.join(role_permission).join(RoleModel).join(user_role).join(UserModel).\
            filter(
            UserModel.id == self.id
        )
        return permissions

同理菜单

代码语言:javascript
复制
class UserModel(db.Model):
    # ...
    @property
    def menus(self):
        menus = MenuModel.query.join(role_menu).join(RoleModel).join(user_role).join(UserModel).\
            filter(
            UserModel.id == self.id
        ).order_by(MenuModel.type_, MenuModel.order).all()
        return menus

这样就可以用user.permissionsuser.menus来获得用户的权限和菜单了

与 Flask 结合

数据库表结构设计好了,下面就是跟 Flask 的结合了

在 Python 中,用 decorator 可以用来做用户验证,比如下面

代码语言:javascript
复制
def auth(method):
    @functools.wraps(method)
    def wrapper(*args, **kwargs):
        user_id = session.get('user_id')
        if not user_id:
            return abort(403)
        return method(*args, **kwargs)
    return wrapper

@app.router('/user/info')
@auth
def user_info():
    return render_template('user/info.html')

上面就是利用 Python 的 decorator 来认证用户,其实也是简单的权限划分

因为在 Flask 中,每个 view 就是一个函数,所以在权限表中,用action来表示每个 view 的函数名,那么每个 view 就是一个最小的权限单位,如果一个角色拥有这个权限,那么他就可以请求这个 view 的操作。所以可以这样验证权限

代码语言:javascript
复制
class UserModel(db.Model):
    # ...
    def check(self, action):
        permission = self.permissions.filter(PermissionModel.action == action).first()
        return bool(permissions)

然后把这个权限验证写到 decorator 里去

代码语言:javascript
复制
permissions = list()

class Permission(object):

    def __init__(self, module=None, action=None):
        self.module = module
        self.action = action

    def check(self, module, func):
        if not self.current_user:
            return False
        return self.current_user.check('{module}.{action}'.format(
            module=module,
            action=func
        ))

    def deny(self):
        return fail(4003, u'无权访问')

    def __call__(self, func):
        permissions.append({
            'action': '{}.{}'.format(func.__module__, func.__name__),
            'name': func.__doc__
        })
        @wraps(func)
        def decorator(*args, **kwargs):
            if not self.check(func.__module__, func.__name__):
                return self.deny()
            return func(*args, **kwargs)
        return decorator

    def __enter__(self):
        if not self.check(self.module, self.action):
            try:
                self.deny()
            except Exception as e:
                raise e
            else:
                raise PermissionDeniedException

    def __exit(self):
        pass

    @property
    def current_user(self):
        return g.user

permission = Permission()

这里参考了 hustazp 的 permission

使用 func.__module__func.__name__ 结合作为权限中的 action,如果单独用 func.__name,肯定会出现相同的函数名,如果加上 func.__module__ 就在一定程度上避免了重合,并且将 func.__doc__ 来作为权限种的 name,还没想到更好的办法来自动加入 name

那么上面的用户认证换成 permission 就是下面

代码语言:javascript
复制
@app.router('/user/info')
@permission
def user_info():
    """用户信息"""
    return render_template('user/info.html')

在开发的过程中,如果写了一个权限就要加到数据库里该有多累,于是就加了一个 permissions,这里把所有的 view 都加到这里面来,然后通过下面的脚本来加入权限

代码语言:javascript
复制
from application.models.user import PermissionModel, RoleModel, role_permission
from application.utils.permissions import permissions
for permission in permissions:
    p = PermissionModel.query.filter_by(action=permission['action']).first()
    if not p:
        p = PermissionModel(
            name=permission['name'],
            action=permission['action']
        )
        db.session.add(p)
        db.session.commit()

role = RoleModel.query.first()  # 这里默认获取一个角色,并且赋予权限
for p in PermissionModel.query.filter_by(is_delete=False):
    r = db.session.query(role_permission).join(RoleModel).join(PermissionModel).\
        filter(
            RoleModel.id == role.id,
            RoleModel.is_delete == False,
            PermissionModel.id == p.id,
            PermissionModel.is_delete == False,
            role_permission.c.is_delete == False,
        ).first()
    if not r:
        role.permissions.append(p)

role.save()
本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • Flask 用户权限划分
    • 基础表#### 用户表
      • 关联表
        • SQLAlchemy
          • 与 Flask 结合
          相关产品与服务
          数据库
          云数据库为企业提供了完善的关系型数据库、非关系型数据库、分析型数据库和数据库生态工具。您可以通过产品选择和组合搭建,轻松实现高可靠、高可用性、高性能等数据库需求。云数据库服务也可大幅减少您的运维工作量,更专注于业务发展,让企业一站式享受数据上云及分布式架构的技术红利!
          领券
          问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档