fastapi依赖与权限管理

概述

fastapi对权限的控制,目前来看有两种,一种是全局权限控制,通过中间件。另一种是通过依赖实现精准权限控制。
个人感觉依赖控制权限有更好的使用环境。
这里以权限控制为例。

依赖的高级用法

官方介绍的依赖的高级用法是通过类的__call__实现传递参数的高级用法,举个例子:

class PermissionChecker:
    """
    权限管理的类型,把闭包改为类实现
    """

    def __init__(self, permissions: Optional[List[str]]):
        """
        传递需要验证是否具有的权限的列表。
        :param permissions:
        """
        self.permissions = permissions

    async def __call__(self, user: User = Depends(user_has_login)) -> User:
        """
        生成的依赖函数
        """
        if user.is_superuser:
            return user
        if not self.permissions:
            return user
        for need_permission in self.permissions:
            db = AdminDatabase().database#通过单例实现的全局数据库调用类
            # 用户->group,group和permission多对多,由auth_group_permission表记录多对多关系字段
            query = select([auth_group_permission]).where(auth_group_permission.c.group_id == user.group_id).where(
                auth_group_permission.c.codename == need_permission)#这个主要是数据库查询是否有权限的过程,由于我使用的异步数据库查询,所以这里没有直接用sqlalchemy的默认查询方式
            res = await db.fetch_all(query)
            if not res:
                raise HTTPException(status_code=403, detail="没有权限")
        return user

讲道理,通过类的实现更直观,不过,当时没看高级依赖写法, 所以我在写代码过程中使用的是闭包的实现方法,代码如下:

def func_user_has_permissions(need_permissions: List[str] = None) -> Callable:
    """
    生成权限认证的依赖
    """

    async def user_has_permission(user: User = Depends(user_has_login)) -> User:
        """
        是否有某权限
        """
        if user.is_superuser:
            return user
        if not need_permissions:
            return user
        for need_permission in need_permissions:
            db = AdminDatabase().database
            query = select([auth_group_permission]).where(auth_group_permission.c.group_id == user.group_id).where(
                auth_group_permission.c.codename == need_permission)
            res = await db.fetch_all(query)
            if not res:
                raise HTTPException(status_code=403, detail="没有权限")
        return user

    return user_has_permission

实现的功能与上方是一致的。两者并没有优劣之分,不过感觉下方实现更pythoner一点???

权限架构

权限最开始考虑的是user对group为多对多,group对permission也是多对多,但是后来发现这样操作会增加数据库查询的次数(1次变3次),且实在是有点复杂,遂改为user对group为多对1,这样只需要查询一次就ok了。为此牺牲一点用户体验是值得的。(除非你要写user-group-permission三联表的多对多表,否则不建议我最开始的实现方式)

from sqlalchemy.orm import relationship
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy import Column, String, Boolean, Integer, ForeignKey, Table, DateTime, DECIMAL, UniqueConstraint, func, text
from datetime import datetime

Base = declarative_base()

class Group(Base):
    __tablename__ = 'fastapi_auth_group'
    id = Column(Integer, primary_key=True, index=True)
    name = Column(String(150), unique=True, index=True, comment="组名")
    def __str__(self):
        return self.name
class User(Base):
    __tablename__ = 'fastapi_auth_user'
    id = Column(Integer, primary_key=True, index=True)
    username = Column(String(150), unique=True, index=True, comment="用户名",nullable=False)
    password = Column(String(128), comment="密码",nullable=False)
    nick_name = Column(String(64), comment="昵称", nullable=False,unique=True)
    qq = Column(String(16), comment="qq")
    email = Column(String(64), nullable=True, unique=True,comment="邮箱")
    phone_number = Column(String(18), nullable=True, unique=True, comment="手机号")
    register_ip = Column(String(32), comment="注册ip")
    register_time = Column(DateTime, comment="注册时间", server_default=func.now())
    status = Column(Integer, comment="状态", server_default='0')
    is_superuser = Column(Boolean, server_default=text('false'), comment="是否为超级管理员")
    is_active = Column(Boolean, server_default=text('true'), comment="是否刻可登录")
    is_delete=Column(Boolean, server_default=text('false'), comment="是否删除")
    group_id=Column(Integer, ForeignKey("fastapi_auth_group.id"))
    def __str__(self):
        return self.username

auth_group_permission = Table(  # 多对多的第三方表,居然还要自己生成。。
    'fastapi_auth_group_permission',
    Base.metadata,
    Column("id",Integer,primary_key=True,index=True),
    Column("group_id", Integer, ForeignKey("fastapi_auth_group.id")),
    Column("codename", String(100), ForeignKey("fastapi_auth_permission.codename")),
    UniqueConstraint('group_id', 'codename', name='idx_group_id_permission_id'),
)


class Permission(Base):
    __tablename__ = 'fastapi_auth_permission'
    name = Column(String(128), unique=True, index=True, comment="权限名称")  # 权限名称
    codename = Column(String(100), comment="权限字段",primary_key=True)  # 权限字段,也是我们平判断权限输入的字段
    groups = relationship("Group", backref="permissions", secondary=auth_group_permission)

    def __str__(self):
        return self.name

示例

所有的权限字段需要自己手动创建,之后会考虑学习django从表自动生成一些增删改查的权限
上面已经有了表,现在讲一下如何在实际操作的时候实现精准的权限控制:
以用户修改密码为例:

# func_user_has_permissions为一个生成depends函数的闭包,通过对应的权限列表生成权限管理
async def modify_password(new_password: ModifyPassword,
                          user: User = Depends(func_user_has_permissions(['user_modify_password']))):
    """
    用户修改密码
    :param current_user:
    :return:
    """
    #这里其实需要先判断旧密码是否正确,各位可以自行增加
    hash_password = get_password_hash(new_password.new_password)#hash化密码,
    #坑爹的sqlalchemy生成的数据库操作指令
    query = User.__table__.update().values({"password": hash_password}).where(User.id == user.id)
    res = await AdminDatabase().database.execute(query)
    return {"code": 200, "message": "success"}

结尾

有任何问题可以到qq群沟通:1067030489

  • 6
    点赞
  • 17
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值