Skip to content

WR-han/drfRBAC

Repository files navigation

基于Django REST framework的 接口级别权限 角色访问控制

  • 自动权限类生成
  • 自动权限表数据生成
  • 自动权限校验
  • 只需着重于权限的分配

目录




  • User - 用户表
  • Role - 角色表 (可作为岗位)
  • Group - 分组表 (可作为部门)
  • Permissions - 权限表
  • UserRole - 用户角色多对多中间表
  • UserPermissions - 用户权限多对多中间表
  • RolePermissions - 角色权限多对多中间表
  • 表关联关系如下:

数据表关系

使用 db_constraint=False 无物理外键


“ 如果一个角色有些像鸭子、且有和鸭子一样游泳的权限、和鸭子一样叫的权限、和鸭子一样的所有权限,那么这个角色就是鸭子。”

  • 角色(Role)在访问控制逻辑中不参与任何逻辑判断,只作为有同类权限用户的合集,方便同角色用户的权限继承

    • e.g.
              客服角色拥有<访问查看客户接口>的权限
              那么所有角色为客服的用户 就都继承有<访问查看客户接口>的权限
              如果用户本身拥有<访问查看客户接口>的权限,那么当前用户在访问控制逻辑中就可以视为客服

  • 只适用于 访问控制逻辑 不适用于 业务逻辑, 如接口内容为查看 角色为客服的所有用户 ,则无法获取到 拥有与客服完全相同权限 用户的数据
  • 权限(Permission)为访问控制的 唯一核心 ,无论什么身份,只要用户拥有接口所需的权限,即可访问该接口,否则 访问会被拦截
  • 用户(User)可通过计算属性 get_permissions 来获取由自己的权限 (UserRole) 和继承自角色(Role)的权限 (RolePermissions) 组成的列表

  • * 进行访问控制前 必须 进行 用户认证,案例详见 Module_Auth.Authentications.RBAC_Authentications => DEMO
  • 使用 drf原生authentication ,用法不过多赘述,若不进行用户认证,则 无法 进行 权限自动验证

  • 所有 接口 如果需要进行访问控制,都可以 / 应该 进行权限判断 (权限类可使用 新的action装饰器 快速创建,并自动生成对应数据,无需 反复手动创建drf权限类)

  1. 当接口 手动 / 自动 配置 权限类 后,便会在 Permission表自动生成 对应该接口的 4条 权限数据 (分别为允许 GET/PUT/POST/DELETE 四种方式请求此接口的权限)
  2. 当用户访问此接口时(已通过drf的 authentication 验证的前提下),若该用户拥有 1.中生成 且与当前 请求方式对应 的权限(UserPermissions表),则可以访问该接口,否则 访问会被拦截


  • django 2.2
  • MySQL 8
  • Django REST framework
  • pymsql
  • PyJWT

  • Module_Key 下创建 key.py 文件 内容如下:
""" 修改token盐 数据库链接信息 抽离 settings.secret_key,settings.allowed_hosts """
RBAC_token_salt = ""

project_database = {
    "default": {
        "ENGINE": "django.db.backends.mysql",
        "NAME": "",
        "USER": "root",
        "PASSWORD": "",
        "HOST": "127.0.0.1",
        "PORT": "3306"
    }
}
project_secret_key = ''
project_allowed_hosts = []

  • migrate并运行项目
  • permissions表会 自动生成 10条权限数据(除 管理员权限 外,其余9条来自DEMO,详见代码 TODO 注释,可方便定位DEMO位置)
... name codeName
... 管理员权限 AdminPermission
... 获取全部用户信息 GET_UserPermission
... 修改全部用户信息 PUT_UserPermission
... 创建全部用户信息 POST_UserPermission
... 删除全部用户信息 DELETE_UserPermission
... 获取特定分组下用户信息 GET_GroupUserPermission
... 修改特定分组下用户信息 PUT_GroupUserPermission
... 创建特定分组下用户信息 POST_GroupUserPermission
... 删除特定分组下用户信息 DELETE_GroupUserPermission
... 获取指定角色用户 GET_role_user

  • 数据库创建 测试用户 数据
  • 为测试用户分配权限 (UserPermissions 表)
  • 使用 /v1/RBAC/Login/ 接口获取 token 👇

登录请求

  • 请求头中 携带token 即可访问DEMO中提供的路由(需要拥有对应权限,否则会被拦截) 👇

token

url name codeName
/v1/RBAC/user/ 获取全部用户信息 GET_UserPermission
修改全部用户信息 PUT_UserPermission
创建全部用户信息 POST_UserPermission
删除全部用户信息 DELETE_UserPermission
/v1/RBAC/user/group_user/ 获取特定分组下用户信息 GET_GroupUserPermission
修改特定分组下用户信息 PUT_GroupUserPermission
创建特定分组下用户信息 POST_GroupUserPermission
删除特定分组下用户信息 DELETE_GroupUserPermission
/v1/RBAC/user/role_user/ 获取指定角色用户 GET_role_user
  • 为用户分配某一权限,该用户即可用此权限对应的 请求方式 访问该权限对应的 接口
  • e.g.
            角色拥有 <获取全部用户信息GET_UserPermission> 权限时 ,即可对 /v1/RBAC/user/ 接口进行 GET 请求
            角色没有 <创建特定分组下用户信息POST_GroupUserPermission> 权限时 ,若对 /v1/RBAC/user/group_user/ 接口进行 POST 请求,则会被拦截



示例1:.../v1/RBAC/user/
示例2:.../v1/RBAC/user/role_user/
示例3:.../v1/RBAC/group_user/role_user/

  • 路由对应的 含义 👇:

示例1:域名/版本号/模块名/所有用户/
示例2:域名/版本号/模块名/所有用户/指定为某一种角色的用户/
示例3:域名/版本号/模块名/某一分组的所有用户/指定为某一种角色的用户/

  • 路由对应的 数据 👇:

示例1:域名/版本号/模块名/针对某张表的数据/
示例2:域名/版本号/模块名/针对某张表的数据/此表数据中的细分数据/
示例3:域名/版本号/模块名/针对某张表某个顶级分类的数据/此分类数据中的细分数据/

  • 路由对应的 生成 👇:

示例1:域名/版本号/模块名/基于ModelViewSet的自动路由/
示例2:域名/版本号/模块名/基于ModelViewSet的自动路由/action生成的自动路由/
示例3:域名/版本号/模块名/基于ModelViewSet的自动路由/action生成的自动路由/

  • 路由对应的 权限 👇:

示例1:域名/版本号/模块名/一级权限/
示例2:域名/版本号/模块名/一级权限/二级权限/
示例3:域名/版本号/模块名/一级权限/二级权限/



基础权限类分为两种,均继承自 drf BasePermission类, 仅供 自定义权限类 继承使用

  • MainPermission
    • 位置Module_Custom.Custom_Permission => MainPermission
  • SecondaryPermission
    • 位置Module_Custom.Custom_Permission => SecondaryPermission

请配置于 Module_Auth.Permissions 中(其中已有DEMO RBAC_Permissions.py
权限类 必须要写 注释 具体原因请查看 👇3. 权限表数据生成
Module_Auth.Permissions 下所有模块中的类,都会在 Permissions表 中自动生成权限数据

  • 具体生成格式请查看 👇3. 权限表数据生成
  • 请确保 Module_Auth.Permissions 下所有模块中的类,都继承自基础权限类(MainPermission /SecondaryPermission ),以免产生无效权限表数据

所有继承自 MainPermission类 的权限类皆为 一级权限类

  • 创建方法
    • 直接继承 MainPermission类 ,如无需重写drf权限类的 has_permission 方法,内部直接 pass 即可
    • 必须要写注释 ,注释内容为此权限的 释义 ,用作权限数据生成,具体请查看 👇3. 权限表数据生成
class UserPermission(MainPermission):
    """
    全部用户信息
    """
    ...
  • 使用方法
    • 一级权限类与drf原有权限类使用方法相同,配置于 permission_classes 之中
    • 一级权限往往表示对 整个视图集所有自动路由接口 的权限控制
class Users(ModelViewSet):

    # 一级权限认证 ↓
    permission_classes = [UserPermission]

    authentication_classes = [UserAuthentication]
    queryset = User.objects.all()
    serializer_class = UserSerializer

所有继承自 SecondaryPermission类 的权限类皆为 二级权限类


  • 创建方法
    • 直接继承 SecondaryPermission类 ,如无需重写drf权限类的 has_permission 方法,内部直接 pass 即可
    • 必须要写 注释 ,注释内容为此权限的 释义 ,用作权限数据生成,具体请查看 👇3. 权限表数据生成
class GroupUserPermission(SecondaryPermission):
    """
    特定分组下用户信息
    """
    ...
  • 使用方法
    • 普通二级权限类 不能 配置于 permission_classes 之中
    • 普通二级权限类需配置于 新action装饰器permission参数 中,具体请查看 👇4. 新action装饰器
    • 二级权限往往表示对 视图集中某个action自动路由接口 的权限控制
    @action(methods=["get"], detail=False, permission=GroupUserPermission, inherit=False)
    def group_user(self, request):
        return Response({
            "code": 200
        })

  • 无需手动创建
  • 使用方法
    • 字符串类型 实参传入 新action装饰器permission参数 中即可,此参数作为该权限的 释义
      • 新action装饰器 会在内部自动生成一个 通用二级权限类 并进行配置
      • 通用二级权限类 的类名为 被装饰方法方法名
    @action(methods=["get"], detail=False, permission="指定角色用户", inherit=True)
    def role_user(self, request):
        return Response({
            "code": 200
        })

👆上述DEMO 会自动生成一个通用二级权限类,其功能 / 自动生成的权限表数据,和下述👇 普通二级权限类 相同

"""伪代码"""
class role_user(SecondaryPermission):
    """
    指定角色用户
    """
    ...

通常情况 下使用 通用二级权限类 即可,更加方便快捷,只需在action装饰器中传入 释义字符串,无需手动创建权限类

  • 而以下情况则需要 手动创建 普通二级权限类:
    • 需要重写权限类的 has_permission 方法
    • 多个二级权限接口 需使用 同一个二级权限 (多个action装饰器 的permission参数传入 同一个 普通二级权限类)
      • 👆此类需求较为常见

一 / 二级权限类皆可 单独使用 (只配置一级权限或二级权限)
同时使用时的验证先后顺序请查看 👇4.2 inherit参数



每一个权限类,会自动生成 四条 权限数据,分别对应四种请求方式 如 UserPermission 权限会生成以下四种权限数据(具体请查看 👇3.2 自动生成方式

  • GET: GET_UserPermission
  • PUT: PUT_UserPermission
  • POST: POST_UserPermission
  • DELETE: DELETE_UserPermission

权限表数据拥有两个主要字段:namecodeName

  • name: 权限的名称,即 释义
  • codeName: 权限的代码,往往由 请求方式_权限类名 组成,供 权限验证 时使用

自动生成来源有两种:

  • 手动创建于 Module_Auth.Permissions 中的权限类,包括 一级权限类普通二级权限类
  • 新action装饰器 中自动生成的通用二级权限类

  • 表字段来源如下:
    • name => f"{请求方式对应的中文}{当前类的注释内容}"
    • codeName => f"{请求方式}_{当前类名}"
class UserPermission(MainPermission):
    """
    全部用户信息
    """
    ...
  • 生成的Permissions表数据:
name codeName
获取全部用户信息 GET_UserPermission
修改全部用户信息 PUT_UserPermission
创建全部用户信息 POST_UserPermission
删除全部用户信息 DELETE_UserPermission
  • 源码位置 APPS.RBAC.apps

  • 表字段来源如下:
    • name => f"{请求方式对应的中文}{新action装饰器permission参数中的 字符串}"
    • codeName => f"{请求方式}_{被新action装饰器所装饰方法的 方法名 }"
  • 生成权限数据数量:
    • 手动创建权限类 生成数据不同,通用二级权限类 只会生成 新action装饰器中 methods参数 允许请求方式 所对应的 权限数据
@action(methods=["get"], detail=False, permission="指定角色用户", inherit=True)
def role_user(self, request):
    return Response({
        "code": 200
    })
  • 生成的Permissions表数据:
name codeName
获取指定角色用户 GET_role_user
  • 源码位置 Module_Custom.Custom_Permission.action

内置二级权限认证,新增两个参数:permissioninherit


是否需要进行二级权限认证

  • 参数类型:二级权限类(class) / 释义 (str)
    • 当参数为 二级权限类 时,直接进行权限验证
    • 当参数为 释义 时,会先根据释义自动生成通用 二级权限类 ,再进行权限验证
  • 参数默认值:None

是否继承一级权限(permission_classes中的权限类)的 认证结果

  • 参数类型:bool
    • 当参数为 True 时,表示继承 (等同于一二级权 限验证结果 关系):
      • 一级权限通过无需进行 二级权限验证
      • 一级权限未通过没有设置一级权限 时, 再进行 二级权限验证
    • 当参数为 Fales 时,表示不继承:
      • 无论 一级权限是否通过 ,只以 二级权限验证结果 为准
  • 参数默认值:True

About

No description, website, or topics provided.

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published

Languages