基于DjangoRESTframework的接口级别权限角色访问控制
github地址????https://githucom/WR-han/drfRBAC期待大佬点星????
自动权限类生成自动权限表数据生成自动权限校验只需着重于权限的分配
User-用户表Role-角色表Group-分组表Permissions-权限表UserRole-用户角色多对多中间表UserPermissions-用户权限多对多中间表RolePermissions-角色权限多对多中间表表关联关系如下:
使用db_constraint=False无物理外键
鸭子角色
“如果一个角色有些像鸭子、且有和鸭子一样游泳的权限、和鸭子一样叫的权限、和鸭子一样的所有权限,那么这个角色就是鸭子。”
角色在访问控制逻辑中不参与任何逻辑判断,只作为有同类权限用户的合集,方便同角色用户的权限继承客服角色拥有<访问查看客户接口>的权限那么所有角色为客服的用户就都继承有<访问查看客户接口>的权限如果用户本身拥有<访问查看客户接口>的权限,那么当前用户在访问控制逻辑中就可以视为客服只适用于访问控制逻辑不适用于业务逻辑,如接口内容为查看角色为客服的所有用户,则无法获取到拥有与客服完全相同权限用户的数据权限为访问控制的唯一核心,无论什么身份,只要用户拥有接口所需的权限,即可访问该接口,否则访问会被拦截用户可通过计算属性get_permissions来获取由自己的权限和继承自角色的权限组成的列表
用户认证
*进行访问控制前必须进行用户认证,案例详见Module_AutAuthentications.RBAC_Authentications=>DEMO使用drf原生authentication,用法不过多赘述,若不进行用户认证,则无法进行权限自动验证
接口级别权限
所有接口如果需要进行访问控制,都可以/应该进行权限判断
权限自动验证机制
当接口手动/自动配置权限类后,便会在Permission表中自动生成对应该接口的4条权限数据当用户访问此接口时,若该用户拥有中生成且与当前请求方式对应的权限,则可以访问该接口,否则访问会被拦截
快速使用
依赖/包
准备
创建key文件
在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条权限数据
… | 管理员权限 | AdminPermission |
… | 获取全部用户信息 | GET_UserPermission |
… | 修改全部用户信息 | PUT_UserPermission |
… | 创建全部用户信息 | POST_UserPermission |
… | 删除全部用户信息 | DELETE_UserPermission |
… | 获取特定分组下用户信息 | GET_GroupUserPermission |
… | 修改特定分组下用户信息 | PUT_GroupUserPermission |
… | 创建特定分组下用户信息 | POST_GroupUserPermission |
… | 删除特定分组下用户信息 | DELETE_GroupUserPermission |
… | 获取指定角色用户 | GET_role_user |
使用
数据库创建测试用户数据为测试用户分配权限使用/v1/RBAC/Login/接口获取token????
请求头中携带token即可访问DEMO中提供的路由????
DEMO提供3个案例路由,对应所需权限如下表:
/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 |
为用户分配某一权限,该用户即可用此权限对应的请求方式访问该权限对应的接口角色拥有<获取全部用户信息GET_UserPermission>权限时,即可对/v1/RBAC/user/接口进行GET请求角色没有<创建特定分组下用户信息POST_GroupUserPermission>权限时,若对/v1/RBAC/user/group_user/接口进行POST请求,则会被拦截
推荐的路由层级
示例www.wrhacn/v1/RBAC/user/示例www.wrhacn/v1/RBAC/user/role_user/示例www.wrhacn/v1/RBAC/group_user/role_user/
路由对应的含义????:
示例域名/版本号/模块名/所有用户/示例域名/版本号/模块名/所有用户/指定为某一种角色的用户/示例域名/版本号/模块名/某一分组的所有用户/指定为某一种角色的用户/
路由对应的数据????:
示例域名/版本号/模块名/针对某张表的数据/示例域名/版本号/模块名/针对某张表的数据/此表数据中的细分数据/示例域名/版本号/模块名/针对某张表某个顶级分类的数据/此分类数据中的细分数据/
路由对应的生成????:
示例域名/版本号/模块名/基于ModelViewSet的自动路由/示例域名/版本号/模块名/基于ModelViewSet的自动路由/action生成的自动路由/示例域名/版本号/模块名/基于ModelViewSet的自动路由/action生成的自动路由/
路由对应的权限????:
示例域名/版本号/模块名/一级权限/示例域名/版本号/模块名/一级权限/二级权限/示例域名/版本号/模块名/一级权限/二级权限/
权限详解
基础权限类
基础权限类分为两种,均继承自drfBasePermission类,仅供自定义权限类继承使用
MainPermission位置:Module_CustoCustom_Permission=>MainPermissionSecondaryPermission位置:Module_CustoCustom_Permission=>SecondaryPermission
自定义权限类
请配置于Module_AutPermissions中权限类必须要写注释具体原因请查看????权限表数据生成Module_AutPermissions下所有模块中的类,都会在Permissions表中自动生成权限数据具体生成格式请查看????权限表数据生成请确保Module_AutPermissions下所有模块中的类,都继承自基础权限类,以免产生无效权限表数据
一级权限类
所有继承自MainPermission类的权限类皆为一级权限类
创建方法直接继承MainPermission类,如无需重写drf权限类的has_permission方法,内部直接pass即可必须要写注释,注释内容为此权限的释义,用作权限数据生成,具体请查看????权限表数据生成
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即可必须要写注释,注释内容为此权限的释义,用作权限数据生成,具体请查看????权限表数据生成
class GroupUserPermission(SecondaryPermission):
'''
特定分组下用户信息
'''
...
使用方法普通二级权限类不能配置于permission_classes之中普通二级权限类需配置于新action装饰器的permission参数中,具体请查看????新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方法多个二级权限接口需使用同一个二级权限????此类需求较为常见
一/二级权限类的使用关系
一/二级权限类皆可单独使用同时使用时的验证先后顺序请查看????2inherit参数
权限表数据生成
生成数量与主要字段
每一个权限类,会自动生成四条权限数据,分别对应四种请求方式如UserPermission权限会生成以下四种权限数据
GET:GET_UserPermissionPUT:PUT_UserPermissionPOST:POST_UserPermissionDELETE:DELETE_UserPermission
权限表数据拥有两个主要字段:name、codeName
name:权限的名称,即释义codeName:权限的代码,往往由请求方式_权限类名组成,供权限验证时使用
自动生成方式
自动生成来源有两种:手动创建于Module_AutPermissions中的权限类,包括一级权限类和普通二级权限类新action装饰器中自动生成的通用二级权限类
手动创建权限类的权限数据生成方式
表字段来源如下:name=>f'{请求方式对应的中文}{当前类的注释内容}'codeName=>f'{请求方式}_{当前类名}'
class UserPermission(MainPermission):
'''
全部用户信息
'''
...
生成的Permissions表数据:
获取全部用户信息 | GET_UserPermission |
修改全部用户信息 | PUT_UserPermission |
创建全部用户信息 | POST_UserPermission |
删除全部用户信息 | DELETE_UserPermission |
源码位置APPS.RBAapps
自动创建权限类的权限数据生成方式
表字段来源如下: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表数据:
获取指定角色用户 | GET_role_user |
新action装饰器
内置二级权限认证,新增两个参数:permission和inherit
是否需要进行二级权限认证
参数类型:二级权限类/释义当参数为二级权限类时,直接进行权限验证当参数为释义时,会先根据释义自动生成通用二级权限类,再进行权限验证参数默认值:None
是否继承一级权限的认证结果
参数类型:bool当参数为True时,表示继承:若一级权限通过则无需进行二级权限验证若一级权限未通过或没有设置一级权限时,再进行二级权限验证当参数为Fales时,表示不继承:无论一级权限是否通过,只以二级权限验证结果为准参数默认值:True
文章为作者独立观点,不代表观点