排序和过滤源码分析
# 继承了GenericAPIView+ListModelMixin,只要在视图类中配置filter_backends它就能实现过滤和排序
	-drf内置的过滤类(SearchFilter),排序类(OrderingFiler)
    -django-filter
    -自定义:写一个类,继承BaseFilterBackend,重写filter_queryset,返回的qs对象,就是过滤或排序后的
    
    
# 只有获取所有才涉及到排序
	-list方法 
    def list(self, request, *args, **kwargs):
        # self.get_queryset()所有数据,经过了self.filter_queryset返回了qs
        # self.filter_queryset完成的过滤
        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)
    
    
    self.filter_queryset完成了过滤,当前在视图类中,self是视图类的对象,去视图类中找没找到,去父类---》GenericAPIView---》filter_queryset
    
        def filter_queryset(self, queryset):
            for backend in list(self.filter_backends):
                queryset = backend().filter_queryset(self.request, queryset, self)
            return queryset
        
        
        
  # 总结:
	-写的过滤类要重写filter_queryset,返回qs(过滤或排序后)对象
    -后期如果不写过滤类,只要在视图类中重写filter_queryset,在里面实现过滤也可以
RBAC介绍
# RBAC  是基于角色的访问控制(Role-Based Access Control )
在 RBAC中,权限与角色相关联,用户通过成为适当角色的成员而得到这些角色的权限。
这就极大地简化了权限的管理。这样管理都是层级相互依赖的,权限赋予给角色,而把角色又赋予用户,这样的权限设计很清楚,管理起来很方便
# RBAC权限管理的模式,最适合公司内部的管理系统,不适合对外互联网用户的系统
	-用户:用户表
    -角色(部门):角色表(部门表)
    -权限:权限表
    
    -所有权限都是存在权限表中一条条的记录(发工资,招聘员工,开员工,发布新版本,开董事会)
    -权限是授予角色的(部门的),一个个角色,就是一条条记录(开发角色,hr角色,股东角色)
    -用户:一个个用户是用户表中一条条记录,用户属于某个部门
    
    -三个表之间的关系
    	-用户和角色关系:多对多,中间表
        -角色和权限关系:多对多,中间表
        -5张表
        	-用户表
            -角色表
            -权限表
            -用户和角色关联表
            -角色和权限关联表
                      
   
 # 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#用户和权限的中间表
RBAC使用
创建表模型models.py
# 创建表模型
from django.db import models
# Create your models here.
class User(models.Model):
    username = models.CharField(max_length=32, verbose_name='用户名')
    password = models.CharField(max_length=32, verbose_name='密码')
    email = models.EmailField(max_length=32, verbose_name='邮箱')
    phone = models.CharField(max_length=32, verbose_name='联系电话')
    address = models.CharField(max_length=64, verbose_name='居住地址')
    class Meta:
        verbose_name = '用户'
        verbose_name_plural = '用户'
    def __str__(self):
        return self.username
class Book(models.Model):
    name = models.CharField(max_length=32, verbose_name='书籍名称')
    price = models.CharField(max_length=32, verbose_name='书籍价格')
    publish_time = models.DateTimeField(auto_now_add=True, verbose_name='图书出版时间')
    class Meta:
        verbose_name = '图书'
        verbose_name_plural = '图书'
    def __str__(self):
        return self.name
class Publish(models.Model):
    name = models.CharField(max_length=32, verbose_name='出版社名称')
    address = models.CharField(max_length=64, verbose_name='出版社地址')
    class Meta:
        verbose_name = '出版社'
        verbose_name_plural = '出版社'
    def __str__(self):
        return self.name    
在admin.py中注册
from django.contrib import admin
# Register your models here.
from .models import *
admin.site.register(User)
admin.site.register(Book)
admin.site.register(Publish)
settings.py修改国际化配置
LANGUAGE_CODE = 'zh-hans'  
TIME_ZONE = 'UTC'
USE_I18N = True
USE_L10N = True
USE_TZ = True
本地访问测试






ACL访问控制列表
定义:规定资源可以被哪些主体进行哪些操作。
在ACL权限模型下,权限管理是围绕资源来设定的。我们可以对不同的页面设定可以访问的用户
RBAC基于角色的访问控制
定义:当一个操作,同时满足a与b时,允许操作。
	a. 规定角色可以对哪些资源进行哪些操作 
	b. 规定主体拥有哪些角色 
RBAC的思想,来源于现实世界的企业结构。比如,销售角色,拥有查看客户信息的权限。当一个销售人员小王入职了,可以把销售角色赋予小王,那么小王就拥有了查看客户的权限。这种方式,避免了ACL模型下,每次新人入职,需要逐个配置资源表的情况。同样,权限变动也变得很方便,只要修改角色,即可实现多用户的权限修改。
ABAC基于属性的访问控制
定义:规定哪些属性的主体可以对哪些属性的资源在哪些属性的情况下进行哪些操作
	ABAC其中的属性就是与主体、资源、情况相关的所有信息。
	主体的属性:指的是与主体相关的所有信息,包括主体的年龄、性别、职位等。
	资源的属性:指的是与资源相关的所有信息,包括资源的创建时间、创建位置、密级等。
	情况的属性:指的是客观情况的属性,比如当前的时间、当前的位置、当前的场景(普通状态、紧急状态)。
	操作:含义还是一样,比如增删改查等。
	设定一个权限,就是定义一条含有四类属性信息的策略(Policy)。
ACL、RBAC、ABAC架构区别
传统的ACL、RBAC的架构是
{subject,action,object},
而ABAC的架构是
{subject,action,object,contextual}且为他们添加了parameter(参数)。
subject属性:比如用户的年龄、部门、角色、威望、积分等主题属性。
action属性:比如查看、读取、编辑、删除等行为属性。
object属性:比如银行账户、文章、评论等对象或资源属性。
contextual属性:比如时段、IP位置、天气等环境属性。
casbin 快速做权限控制
pip install casbin
import casbin
e = casbin.Enforcer("./model.conf", "./policy.csv")
sub = "jason"  # 想要访问资源的用户
obj = "book"  # 将要被访问的资源
act = "get"  # 用户对资源进行的操作
# 自己写acl的控制
# 当前用户id,去权限和用户表查询即可,有记录就是有权限
# 自己写rbac
# 当前用户id,找到他的角色,根据角色拿出权限,判断当前访问有没有
if e.enforce(sub, obj, act):
    # 允许alice读取data1
    print('有权限')
else:
    # 拒绝请求,抛出异常
    print('没有权限')
model.conf
[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
policy.csv
p,alice,data1,read
p,bob,data2,write
p,jason,book,get
后台管理simplu的使用
安装使用
安装命令:
	pip3 install django-simpleui 
    
镜像源:
    豆瓣:http://pypi.douban.com/simple/
    中科大:https://pypi.mirrors.ustc.edu.cn/simple/
    清华:https://pypi.tuna.tsinghua.edu.cn/simple
        
官方教程:
    https://simpleui.72wo.com/docs/simpleui/quick.html 
settings.py文件中INSTALLED_APPS的第一行加入
INSTALLED_APPS = [
        'simpleui',
        'django.contrib.admin',
        'django.contrib.auth',
        'django.contrib.contenttypes',
        'django.contrib.sessions',
        'django.contrib.messages',
        'django.contrib.staticfiles',
        'app01.apps.App01Config',
        'rest_framework',
    ]
创建表模型models.py
# 创建表模型
from django.db import models
# Create your models here.
class User(models.Model):
    username = models.CharField(max_length=32, verbose_name='用户名')
    password = models.CharField(max_length=32, verbose_name='密码')
    email = models.EmailField(max_length=32, verbose_name='邮箱')
    phone = models.CharField(max_length=32, verbose_name='联系电话')
    address = models.CharField(max_length=64, verbose_name='居住地址')
    class Meta:
        verbose_name = '用户'
        verbose_name_plural = '用户'
    def __str__(self):
        return self.username
class Book(models.Model):
    name = models.CharField(max_length=32, verbose_name='书籍名称')
    price = models.CharField(max_length=32, verbose_name='书籍价格')
    publish_time = models.DateTimeField(auto_now_add=True, verbose_name='图书出版时间')
    class Meta:
        verbose_name = '图书'
        verbose_name_plural = '图书'
    def __str__(self):
        return self.name
class Publish(models.Model):
    name = models.CharField(max_length=32, verbose_name='出版社名称')
    address = models.CharField(max_length=64, verbose_name='出版社地址')
    class Meta:
        verbose_name = '出版社'
        verbose_name_plural = '出版社'
    def __str__(self):
        return self.name    
修改配置文件settings.py
LANGUAGE_CODE = 'zh-hans'
TIME_ZONE = 'Asia/Shanghai'
USE_I18N = True
USE_L10N = True
USE_TZ = True
# Static files (CSS, JavaScript, Images)
# https://docs.djangoproject.com/en/2.2/howto/static-files/
STATIC_URL = '/static/'
STATICFILES_DIRS = [
    os.path.join(BASE_DIR, 'static')
    
# 定制左侧菜单栏
    
  
import time  
    
SIMPLEUI_CONFIG = {
    'system\_keep': False,
    'menu\_display': ['监控大屏','应用1', '权限认证', '测试', '动态菜单测试'],  # 开启排序和过滤功能, 不填此字段为默认排序和全部显示, 空列表[] 为全部不显示.
    'dynamic': True,  # 设置是否开启动态菜单, 默认为False. 如果开启, 则会在每次用户登陆时动态展示菜单内容
    'menus': [
        {
            'name': '监控大屏',
            'icon': 'fas fa-code',
            'url': '/index/'
        },
        {
            'app': 'app01',
            'name': '应用1',
            'icon': 'fas fa-user-shield',
            'models': [
                {
                    'name': '图书',
                    'icon': 'fa fa-user',
                    'url': 'app01/book/'
                },
                {
                    'name': '用户',
                    'icon': 'fa fa-user',
                    'url': 'app01/userinfo/'
                }
            ]
        },
        {
            'app': 'auth',
            'name': '权限认证',
            'icon': 'fas fa-user-shield',
            'models': [
                {
                    'name': '用户',
                    'icon': 'fa fa-user',
                    'url': 'auth/user/'
                },
                {
                    'name': '用户组',
                    'icon': 'fa fa-user',
                    'url': 'auth/group/'
                }
            ]
        },
        {
            # 自2021.02.01+ 支持多级菜单,models 为子菜单名
            'name': '测试',
            'icon': 'fa fa-file',
            # 二级菜单
            'models': [{
                'name': 'Baidu',
                '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': '内网穿透',
                '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'
            }]
        }
    ]
}
在admin.py中注册
from django.contrib import admin
# Register your models here.
from .models import *
admin.site.register(User)
admin.site.register(Book)
admin.site.register(Publish)
视图类views.py
def index(request):
    return render(request, 'index.html', {'count': 9999})
配置路由
    path('index/', views.index),
自定义html页面
<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta http-equiv="X-UA-Compatible" content="ie=edge">
  <link rel="stylesheet" href="/static/css/four.css">
  <link rel="stylesheet" href="/static/fonts/iconfont.css">
  <!-- <script src="js/echarts.min.js"></script> -->
  <script src="/static/js/jquery.min.js"></script>
  <script src="/static/js/macarons.js"></script>
  <!-- <script src="js/china.js"></script> -->
  <script src="/static/js/countUp.js"></script>
</head>
<body>
    <ul class="nav">
    
        <li class="drop-down">
          <a href="#"></a>
      
          <ul class="drop-down-content">
      
            <li>
              <a href="./home.html">自动切换</a>
            </li>
      
            <li>
              <a href="./index.html">第一屏</a>
            </li>
      
            <li>
              <a href="./second.html">第二屏</a>
            </li>
            <li>
              <a href="./third.html">第三屏</a>
            </li>
      
            <li>
              <a href="./four.html?index=0">第四屏</a>
            </li>
          </ul>
        </li>
      </ul>
  <div class="first-screen root-wrap">
    <header>
      <img src="/static/img/cnonix.png" />
      <div class="title_img">
        <img src="/static/img/title_left_img.png" alt="">
      </div>
      <!-- <span class="month-tip">2017年1月</span> -->
    </header>
    <div class="main clearfix">
      <!-- 同类图书 -->
      <div class="aside-left fl">
        <h3>同类图书</h3>
        <ul class="clearfix">
        </ul>
      </div>
      <div class="middle_con fl">
        <div class="middle_top">
          <div class="con_left fl">
          </div>
          <div class="con_right fl clearfix">
            <dl class="clearfix">
              <dt>图书标签</dt>
              <dd class="clearfix">
                <ul class="bookLabel clearfix"></ul>
              </dd>
            </dl>
            <dl class="clearfix">
              <dt class="clearfix">主题词</dt>
              <dd class="clearfix">
                <ul class="themaWords clearfix"></ul>
              </dd>
            </dl>
          </div>
          <div class="con_link">
            <h3>内容提要</h3>
            <div class="link_title  link_con">
              <!-- 钱锺书所著的《围城》是一幅栩栩如生的世井百态图,人生的酸甜苦辣千般滋味均在其中得到了淋漓尽致的体现。钱钟书先生将自己的语言天才并入极其渊博的知识,再添加上一些讽刺主义的幽默调料,以一书而定江山。《围城》显示给我们一个真正的聪明人是怎样看人生,又怎样用所有作家都必得使用的文字来表述自己的“观”和“感”的。 -->
            </div>
          </div>
          <div class="con_link">
            <h3>本书目录</h3>
            <ul class="link_ul">
              <!-- <li>1.序</li>
                <li>2.围城</li>
                <li>3.围城</li>
                <li>4.记钱钟书与《围城》——杨绛</li> -->
            </ul>
          </div>
          <div class="con_link">
            <h3>作者介绍</h3>
            <div class="link_title link_author"></div>
          </div>
        </div>
        <div class="middle_button">
          <div class="button_left">
            <h3>购买推荐</h3>
            <div class="button_con">
              <div class="container purchase claefix">
              </div>
            </div>
          </div>
          <div class="button_right">
            <h3>借阅推荐</h3>
            <div class="button_con">
              <div class="container jieyue claefix">
              </div>
            </div>
          </div>
        </div>
      </div>
      <div class="aside-right clearfix fl">
        <div class="con_top clearfix">
          <h3>图书评论</h3>
          <div class="aside_con clearfix">
          </div>
        </div>
      </div>
    </div>
    <div class="main-bottom">
      <span class="line1"></span>
      <span class="line2"></span>
      <span class="line3"></span>
    </div>
  </div>
  <script src="/static/js/four.js"></script>
</body>
</html>
本地访问测试



restframework-jwt执行流程分析
# 签发的token,有过期时间,过期时间是?配置一般设为7天
JWT_AUTH = {
    'JWT_EXPIRATION_DELTA': datetime.timedelta(days=7),
}
# 双token认证
	-用户正在app或者应用中操作 token突然过期,此时用户不得不返回登陆界面,重新进行一次登录,这种体验性不好,于是引入双token校验机制
    -实现原理:首次登陆时服务端返回两个token ,accessToken和refreshToken,accessToken过期时间比较短,refreshToken时间较长,且每次使用后会刷新,每次刷新后的refreshToken都是不同
   
    
    -refreshToken假设7天,accessToken过期时间5分钟
    -正常使用accessToken即可,如果accessToken过期了,重新发请求,携带refreshToken过来,能正常返回,并且这次响应中又带了acessToken
    
# django中顶格写的代码,都会执行
    
# 签发流程--》本质就是登录接口---》【校验用户是否正确,如果正确签发token】写到了序列化类中,如果不正确返回错误
	-obtain_jwt_token:核心代码--ObtainJSONWebToken.as_view()
    -ObtainJSONWebToken 视图类,实现了登录功能
    class ObtainJSONWebToken(JSONWebTokenAPIView):
    	serializer_class = JSONWebTokenSerializer
    -父类ObtainJSONWebToken
    class JSONWebTokenAPIView(APIView):
        # 局部禁用掉权限和认证
        permission_classes = () 
        authentication_classes = ()
        def get_serializer_context(self):
            return {
                'request': self.request,
                'view': self,
            }
        def get_serializer_class(self):
            return self.serializer_class
        def get_serializer(self, *args, **kwargs):
            serializer_class = self.get_serializer_class()
            kwargs['context'] = self.get_serializer_context()
            return serializer_class(*args, **kwargs)
        def post(self, request, *args, **kwargs):
            # JSONWebTokenSerializer实例化得到一个序列号类的对象,传入前端传的只
            serializer = self.get_serializer(data=request.data)
            if serializer.is_valid(): # 校验前端传入的数据是否合法:
                #1 字段自己的规则 2 局部钩子 3 全局钩子(序列化类的validate方法)
                # 获取当前登录用户和签发token是在序列化类中完成的
                # 从序列化类对象中取出了当前登录用户
                user = serializer.object.get('user') or request.user
                # # 从序列化类对象中取出了token
                token = serializer.object.get('token')
                # 自定义过
                response_data = jwt_response_payload_handler(token, user, request)
                response = Response(response_data)
                return response
            return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
      -序列化类:JSONWebTokenSerializer
    	class JSONWebTokenSerializer(Serializer):
            def validate(self, attrs):
                credentials = {
                    'username': attrs.get('username'),
                    'password': attrs.get('password')
                }
                if all(credentials.values()):
                    # auth的校验用户名和密码是否正确
                    user = authenticate(**credentials)
                    if user:
                        # 通过用户获得payload:{}
                        payload = jwt_payload_handler(user)
                        return {
                            'token': jwt_encode_handler(payload),
                            'user': user
                        }
                    else:
                        # 根据用户名和密码查不到用户
                        raise serializers.ValidationError(msg)
                else:	
                    # 用户名和密码不传,传多了都不行
                    raise serializers.ValidationError(msg)
                    
# 认证
	-认证类;JSONWebTokenAuthentication
    class JSONWebTokenAuthentication(BaseJSONWebTokenAuthentication):
        def get_jwt_value(self, request):
            # get_authorization_header(request)根据请求头中HTTP_AUTHORIZATION,取出token
            # jwt adsfasdfasdfad
            # auth=['jwt','真正的token']
            auth = get_authorization_header(request).split()
            auth_header_prefix = api_settings.JWT_AUTH_HEADER_PREFIX.lower()
            if not auth:
                if api_settings.JWT_AUTH_COOKIE:
                    return request.COOKIES.get(api_settings.JWT_AUTH_COOKIE)
                return None
            if smart_text(auth[0].lower()) != auth_header_prefix:
                return None
            if len(auth) == 1:
                msg = _('Invalid Authorization header. No credentials provided.')
                raise exceptions.AuthenticationFailed(msg)
            elif len(auth) > 2:
                msg = _('Invalid Authorization header. Credentials string '
                        'should not contain spaces.')
                raise exceptions.AuthenticationFailed(msg)
            return auth[1]
        
   -父类中:BaseJSONWebTokenAuthentication---》authenticate
class BaseJSONWebTokenAuthentication(BaseAuthentication):
    def authenticate(self, request):
        # jwt_value前端传入的token
        jwt_value = self.get_jwt_value(request)
        # 前端没有传入token,return None,没有带token,认证类也能过,所有咱们才加权限类
        if jwt_value is None:
            return None
        try:
            payload = jwt_decode_handler(jwt_value) # 验证token,token合法,返回payload
        except jwt.ExpiredSignature:
            msg = _('Signature has expired.')
            raise exceptions.AuthenticationFailed(msg)
        except jwt.DecodeError:
            msg = _('Error decoding signature.')
            raise exceptions.AuthenticationFailed(msg)
        except jwt.InvalidTokenError:
            raise exceptions.AuthenticationFailed()
        user = self.authenticate_credentials(payload) # 通过payload得到当前登录用户
        return (user, jwt_value) # 后期的request.user就是当前登录用户
    
    
 # 它这个认证类:只要带了token,request.user就有只,如果没带token,不管了,继续往后走