drf之rabc,后台管理simplui
一、过滤类和排序类源码分析
# 我们之前在学习排序和过滤类的时候是在继承了GenericAPIView+ListModelMixin的视图类中才能够配置的 # 然后在视图类中的filter_backends属性中上写排序类和过滤类就能够实现 # 可以配置在这个属性的有: -drf内置的过滤类(SearchFilter),排序类(OrderingFiler) -第三方模块django-filter -自定义类:写一个类,继承BaseFilterBackend,重写filter_queryset,返回的qs对象,就是过滤或排序后的 # 然后只有获取全部才会涉及到排序 所以肯定会有list方法 所以我们可以去看一下ListModelMixin的源码 class ListModelMixin: def list(self, request, *args, **kwargs): queryset = self.filter_queryset(self.get_queryset()) # 这个就是从视图类中我们配置的排序和过滤属性配置的类中提取 page = self.paginate_queryset(queryset) # 从视图类分页属性获取分页类 # 这下面就是分页功能 if page is not None: # 判断是否为空 serializer = self.get_serializer(page, many=True) return self.get_paginated_response(serializer.data) serializer = self.get_serializer(queryset, many=True) return Response(serializer.data) # 返回分页和排序过后的数据 # 然后filter_queryset在这个类中没有这个方法和视图类中我们也没有写这个方法,所以我们要去GenericAPIView类中找这个方法 然后看一下这个方法写了啥 def filter_queryset(self, queryset): for backend in list(self.filter_backends): # 从视图类中filter_backends一个一个循环出来赋值给backend queryset = backend().filter_queryset(self.request, queryset, self) # 然后这里又利用了递归函数 把数据给过滤和排序完 return queryset # 最后返回出去 # 我们可以看到在循环取值的时候 源码用了list强行把变成列表 这样就不会循环报错 class LoginView(GenericAPIView): filter_backends = BaseFilterView # 我们在配置的时候也可以不用加列表也不会报错 # 我们还可以在视图类中重写filter_queryset方法 这样也就有提供了另一种过滤方法和排序方法
二、基于jwt的认证类
class JWTAuthentication(BaseAuthentication): def authenticate(self, request): # 放到头中:token-->HTTP_TOKEN Authorization--->HTTP_AUTHORIZATION print(request.META) jwt_value = request.META.get('HTTP_TOKEN') # 验证token是否合法,jwt模块下一定有个验证token的函数 try: payload = jwt_decode_handler(jwt_value) except jwt.ExpiredSignature: raise AuthenticationFailed('token过期了') except jwt.DecodeError: raise AuthenticationFailed('token解码失败') except jwt.InvalidTokenError: raise AuthenticationFailed('认证失败') # 执行到这,说明token合法,payload可以使用 user_id = payload.get('user_id') user = UserInfo.objects.filter(pk=user_id).first() # 每次都要查数据库,效率不太好 return (user, jwt_value) # return (payload,jwt_value) # 我们还可以自己返回payload这样后续使用的时候就不会再走数据库了
三、RABC介绍和使用
# RBAC 是基于角色的访问控制(Role-Based Access Control )在 RBAC 中, -权限与角色相关联,用户通过成为适当角色的成员而得到这些角色的权限。 这就极大地简化了权限的管理。这样管理都是层级相互依赖的,权限赋予给角色,而把角色又赋予用户,这样的权限设计很清楚,管理起来很方便 # RBAC权限管理的模式,最适合公司内部的管理系统,不适合对外互联网用户的系统 -用户:用户表 -角色(部门):角色表(部门表) -权限:权限表 -所有权限都是存在权限表中一条条的记录(发工资,招聘员工,开员工,发布新版本,开董事会) -权限是授予角色的(部门的),一个个角色,就是一条条记录(开发角色,hr角色,股东角色) -用户:一个个用户是用户表中一条条记录,用户属于某个部门 # 三个表之间的关系 用户与角色是多对多关系 中间表 角色与权限是多对多关系 中间表 所以总共有五张表 用户表 角色表 权限表 用户与角色中间表 角色与权限中间表 # 举例子: 用户表: id 姓名 1 张三 2 里斯 角色表 id 角色名称 1 hr角色 2 股东角色 3 开发角色 权限表 id 权限名词 1 发工资 2 招人 3 提交代码 #####张三要有提交代码的权限 用户和角色中间表: id 角色id 用户id 1 3 1 权限和角色的中间表 id 角色id 权限id 1 3 3 # 就是如果要判断一个用户有什么权限那么要先知道这个用户是什么角色 然后通过角色知道这个用户有什么权限
django的rbac
# django的后台管理admin就自带了rbac的权限,通过auth模块实现的,比普通rbac更高级一些 # 本来5张表 -django是6张表,用户和权限的多对多关系表(一个用户可以分配多个权限,一个权限可以给多个用户) -6张表了 -用户表 -角色表 -权限表 -用户和角色关联表 -角色和权限关联表 -用户和权限的多对多关系表 ''' 就是如果一个用户想要一个他本身角色外的权限 就需要把这个用户与相对于的角色绑定 而如果该角色的权限有很多 ,那么给这个用户就会有点多,只需要给他这个角色的权限之一即可 ,所以就有了用户与权限的关联表 一个权限就可以与一个用户绑定 ''' -启用了admin和auth,这6张表就迁移进去了 auth_user # 用户表 auth_group # 角色,组,部门表 auth_permission # 权限表 auth_user_groups # 用户和角色中间表 auth_group_permissions # 角色跟权限中间表 auth_user_user_permissions #用户和权限的中间表
# ACL(Access Control List,访问控制列表) 将用户或组等使用者直接与对象的权限对接。 -用户表,权限表,中间 给用户授予某些权限即可 # RBAC(Role-Based Access Control,基于角色的访问控制) 将用户与角色对接,然后角色与对象的权限对接 # RBAC+ACL django,公司用的比较多啊 # ABAC(Attribute-Based Access Control,基于属性的访问控制) ABAC(Attribute-Based Access Control,基于属性的访问控制) 又称为PBAC(Policy-Based Access Control,基于策略的访问控制) CBAC(Claims-Based Access Control,基于声明的访问控制) 传统的ACL、RBAC的架构是 {subject,action,object}, 而ABAC的架构是 {subject,action,object,contextual}且为他们添加了parameter(参数)。 subject属性:比如用户的年龄、部门、角色、威望、积分等主题属性。 action属性:比如查看、读取、编辑、删除等行为属性。 object属性:比如银行账户、文章、评论等对象或资源属性。 contextual属性:比如时段、IP位置、天气等环境属性。 权限表 id 权限名 1 开除员工 id 权限名 属性 1 开除员工 女 1 开除员工 男
代码演示
# 需要先下载模块casbin pip3 install casbin import casbin e = casbin.Enforcer("./model.conf", "./policy.csv") # 这个三个参数的值是我们在policy.csv写好的 sub = "lqz" # 想要访问资源的用户 obj = "book" # 将要被访问的资源 act = "get" # 用户对资源进行的操作 # 上面的意思就是 用户lqz通过get请求访问book是可以的 如果是 其他请求就不能访问 ,访问其他接口也不能访问 # 自己写acl的控制 # 当前用户id,去权限和用户表查询即可,有记录就是有权限 # 自己写rbac # 当前用户id,找到他的角色,根据角色拿出权限,判断当前访问有没有 if e.enforce(sub, obj, act): # 允许alice读取data1 print('有权限') else: # 拒绝请求,抛出异常 print('没有权限')
更多知识点:https://docs.casbin.cn/zh/docs/overview
[request_definition] r = sub, obj, act [policy_definition] p = sub, obj, act [policy_effect] e = some(where (p.eft == allow)) [matchers] m = r.sub == p.sub && r.obj == p.obj && r.act == p.act
p,alice,data1,read p,bob,data2,write p,lqz,book,get # lqz用户,book接口,get请求 # 这个就是相当于 一个一个字段
# django admin自带了权限控制,但是是前后端混合的,我们可以二次开发,开发出公司内部的自动化运行,自动化测试,人事管理系统,订单系统。。。。样子不好看 # 对django admin进行美化 -xadmin(不用了,过时了) -simpleui(正红) # 基于drf+vue 自己写前后端分离的权限管理 # go-vue-admin # 使用simpleui可以美化后台管理的页面
代码
# 要先下载 pip install django-simpleui import time SIMPLEUI_CONFIG = { 'system_keep': False, 'menu_display': ['我的首页', '图书管理','权限认证', '多级菜单测试', '动态菜单测试'], # 开启排序和过滤功能, 不填此字段为默认排序和全部显示, 空列表[] 为全部不显示. 'dynamic': True, # 设置是否开启动态菜单, 默认为False. 如果开启, 则会在每次用户登陆时动态展示菜单内容 'menus': [ { 'name': '我的首页', 'icon': 'fas fa-code', 'url': '/index/', }, { 'app': 'app01', 'name': '图书管理', 'icon': 'fas fa-code', 'models': [ { 'name': '用户', 'icon': 'fa fa-user', 'url': 'app01/userinfo/' }, { 'name': '图书', 'icon': 'fa fa-user', 'url': 'app01/book/' }, { 'name': '出版社', 'icon': 'fa fa-user', 'url': 'app01/publish/' }, ] }, { 'app': 'auth', 'name': '权限认证', 'icon': 'fas fa-user-shield', 'models': [{ 'name': '用户', 'icon': 'fa fa-user', 'url': 'auth/user/' }] }, { # 自2021.02.01+ 支持多级菜单,models 为子菜单名 'name': '多级菜单测试', 'icon': 'fa fa-file', # 二级菜单 'models': [ { 'name': '百度', 'icon': 'far fa-surprise', # 第三级菜单 , 'models': [ { 'name': '爱奇艺', 'url': 'https://www.iqiyi.com/dianshiju/' # 第四级就不支持了,element只支持了3级 }, { 'name': '百度问答', 'icon': 'far fa-surprise', 'url': 'https://zhidao.baidu.com/' } ] }, { 'name': 'lqz', 'url': 'https://www.wezoz.com', 'icon': 'fab fa-github' }] }, { 'name': '动态菜单测试', 'icon': 'fa fa-desktop', 'models': [{ 'name': time.time(), 'url': 'http://baidu.com', 'icon': 'far fa-surprise' }] }] } SIMPLEUI_LOGIN_PARTICLES = False SIMPLEUI_HOME_INFO = False
from django.contrib import admin # Register your models here. from .models import * @admin.register(Book) class BookAdmin(admin.ModelAdmin): list_display = ('id', 'title', 'price') # 增加自定义按钮 actions = ['make_copy', ] def make_copy(self, request, queryset): print('adsfasdf') make_copy.short_description = '自定义按钮' # # icon,参考element-ui icon与https://fontawesome.com make_copy.icon = 'fas fa-audio-description' # # 指定element-ui的按钮类型,参考https://element.eleme.cn/#/zh-CN/component/button make_copy.type = 'danger' make_copy.confirm = '你是否执意要点击这个按钮?' admin.site.register(UserInfo) admin.site.register(Publish)
我们使用了