day73

 

上节回顾

1 过滤的源码分析
-视图类中配置类属性:filter_backends = ['过滤类']
   -必须继承他俩ListModelMixin+GenericAPIView
   -ListModelMixin的list方法中执行了self.filter_queryset,视图类
   -GenericAPIView找filter_queryset方法
   for backend in list(self.filter_backends):
      queryset = backend().filter_queryset(self.request, queryset, self)
       return queryset
   
   
2 分页类的源码分析
-视图类中配置类属性: pagination_class = 分页类
   -必须继承他俩ListModelMixin+GenericAPIView
   -ListModelMixin的list方法中执行了
   page = self.paginate_queryset(queryset) # 分页
   if page is not None:
      serializer = self.get_serializer(page, many=True)
      return self.get_paginated_response(serializer.data)
   
   -GenericAPIView找paginate_queryset方法
  #self.paginator配置的分页类的对象
  self.paginator.paginate_queryset(queryset, self.request, view=self)
   -GenericAPIView找get_paginated_response方法
  #self.paginator配置的分页类的对象
self.paginator.get_paginated_response(data)
 -具体的分页类:PageNumberPagination
-paginate_queryset
   -get_paginated_response
   
   
3 继承APIView实现分页
-获取了所有数据
   -分页
  -实例化得到分页对象
       -调用分页对象的paginate_queryset方法完成分页
       -把要分页的数据序列化
       -把序列化后的数据返回(直接返回和使用get_paginated_response)
       
4 自定义分页(出去面试,面试官问:如何自定义分页)
-使用当此请求的request对象,取出获取的页码数
   -通过页码数和每页显示多少条,具体的取出当前页码的数据

5 jwt:json web token,是一种前后端的登录认证方式,它分token的签发和认证,签发的意思是用户登录成功,生成三段式的token串;认证指的是用户访问某个接口,需要携带token串过来,我们完成认证
6 三段:头.荷载.签名,每一段都通过base64编码
7 base64的编码和解码
8 djangorestframework-jwt:好久没维护了,使用djangorestframework-simplejwt
9 快速使用
-快速签发
  -在路由中(登录功能有了,基于auth的user表实现的登录)
       path('login/', obtain_jwt_token)
   -快速认证
  -在视图类中配置
       authentication_classes = [JSONWebTokenAuthentication, ]#drf_jwt
  permission_classes = [IsAuthenticated] # drf

 

今日内容

1 jwt内置签发方法修改返回格式(***)

# 写一个函数
def jwt_response_payload_handler(token, user=None, request=None):
   return {
       'token':token,
       'username':user.username
  }
# 在配置文件中配置
#drf_jwt的配置文件(drf_jwt有个默认配置文件:from rest_framework_jwt import settings)
from rest_framework_jwt.utils import jwt_response_payload_handler
JWT_AUTH={
   'JWT_RESPONSE_PAYLOAD_HANDLER':'app01.utils.jwt_response_payload_handler'
}

 

1.2 jwt签发的源码分析

# 路由的---》obtain_jwt_token---》ObtainJSONWebToken.as_view()---》视图类ObtainJSONWebToken----》
class ObtainJSONWebToken(JSONWebTokenAPIView):
   # 序列化类
   serializer_class = JSONWebTokenSerializer
   
# 登录请求,发了一个post请求,携带用户名密码---》ObtainJSONWebToken的post请求
# JSONWebTokenAPIView内有个post
   def post(self, request, *args, **kwargs):
       # 得到一个序列化类的对象,使用传入的request.data
       serializer = self.get_serializer(data=request.data)
       # 序列化类对象的is_valid,会执行字段自己的校验规则,局部钩子,全局钩子
       if serializer.is_valid():
           # 取出user,取出token
           user = serializer.object.get('user') or request.user
           token = serializer.object.get('token')
           # 执行你在配置文件中配置的函数
           response_data = jwt_response_payload_handler(token, user, request)
           response = Response(response_data)
      return response
   
   
# JSONWebTokenSerializer源码
-validate
       def validate(self, attrs):
       credentials = {
           self.username_field: attrs.get(self.username_field),
           'password': attrs.get('password')
      }
       # {username:lq,password:lqz12345}
       if all(credentials.values()):
           user = authenticate(**credentials)  # 通过用户名密码认证auth的user表中的用户
           if user:
               # 通过user得到荷载{user_id:1,username:lqz,..}
               payload = jwt_payload_handler(user)
               return {
                   'token': jwt_encode_handler(payload), # 通过荷载得到token串
                   'user': user
              }
         

1.3 drf_jwt认证token源码

# 1 读JSONWebTokenAuthentication的父类BaseJSONWebTokenAuthentication的authenticate
   def authenticate(self, request):
       # 取出前端传入的token串
       jwt_value = self.get_jwt_value(request)
       if jwt_value is None: # 如果token串,前端没有传,也认证通过,后续必须加权限类
           return None

       try:
           payload = jwt_decode_handler(jwt_value) # 通过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()

       # 通过payload获得当前用户:通过user_id---->auth的user表中获取当前用户
       user = self.authenticate_credentials(payload)

       return (user, jwt_value)

# 2 JSONWebTokenAuthentication的get_jwt_value方法,返回token串
   def get_jwt_value(self, request):
       # 取出前端传入的token串,通过空格切分
       #(jwt,token串)
       auth = get_authorization_header(request).split()
       auth_header_prefix = api_settings.JWT_AUTH_HEADER_PREFIX.lower()

       if not auth: # 如果没有传认证的token,返回None
           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: # auth元组的长度如果等于1,抛异常,因为我们jwt token串
           msg = _('Invalid Authorization header. No credentials provided.')
           raise exceptions.AuthenticationFailed(msg)
       elif len(auth) > 2: # auth元组的长度如果大于1,抛异常,因为我们jwt token串
           msg = _('Invalid Authorization header. Credentials string '
                   'should not contain spaces.')
           raise exceptions.AuthenticationFailed(msg)

       return auth[1]

 

2 自定义User表实现jwt的token签发(****)

from rest_framework.response import Response
from .models import UserInfo
from rest_framework_jwt.settings import api_settings

jwt_payload_handler = api_settings.JWT_PAYLOAD_HANDLER
jwt_encode_handler = api_settings.JWT_ENCODE_HANDLER


class LoginView(APIView):
   def post(self, request):
       username = request.data.get('username')
       password = request.data.get('password')
       user = UserInfo.objects.filter(username=username, password=password).first()
       if user:  # 用户名和密码正确,登录成功,签发token
           payload = jwt_payload_handler(user)
           token = jwt_encode_handler(payload)
           return Response({'code': '100', 'msg': '登录成功', 'token': token, 'usrename': user.username})
       else:
           return Response({'code': '101', 'msg': '用户名或密码错误'})

 

3 自定义认证类,实现jwt的token认证(****)

from rest_framework.authentication import BaseAuthentication
from rest_framework import exceptions
import jwt
from .models import UserInfo
from rest_framework_jwt.settings import api_settings
jwt_decode_handler = api_settings.JWT_DECODE_HANDLER
class JwtAuthentication(BaseAuthentication):
   def authenticate(self, request):
       # 取出前端传入的token串
       token=request.META.get('HTTP_TOKEN')
       # 通过token获得payload
       try:
           payload = jwt_decode_handler(token) # 通过token串获得payload,验签(是否被篡改),检查过期时间
       # except Exception:
       #     raise exceptions.AuthenticationFailed('token认证失败')
       except jwt.ExpiredSignature:
           msg = '签名过期'
           raise exceptions.AuthenticationFailed(msg)
       except jwt.DecodeError:
           msg ='解码错误'
           raise exceptions.AuthenticationFailed(msg)
       except jwt.InvalidTokenError:
           raise exceptions.AuthenticationFailed()
       # 通过payload获得当前用户(自己的表,自己拿)
       # user=UserInfo.objects.filter(pk=payload['user_id']).first()
       
       
       # 稍微优化一下,不是每次都取查询当前登录用户
       # user={'id':payload['user_id'],'username':payload['username']}
       user=UserInfo(id=payload['user_id'],username=payload['username'])
       # 返回当前用户
       return user,token

 

4 RBAC介绍(****)

# 基于角色的访问控制:RBAC  是基于角色的访问控制(Role-Based Access Control )在 RBAC  中,权限与角色相关联,用户通过成为适当角色的成员而得到这些角色的权限。这就极大地简化了权限的管理。这样管理都是层级相互依赖的,权限赋予给角色,而把角色又赋予用户,这样的权限设计很清楚,管理起来很方便。

# rbac权限控制一般都是用在公司内部的管理系统中(python的django写公司内部项目较多,rbac很重要)
# Django的 Auth组件 采用的认证规则就是RBAC

# rbac表分析
-用户表:auth_user
   -角色表(部门,组表):auth_group
   -权限表:auth_permission
   
   -角色和权限是多对多---》中间表:auth_group_permissions
   -用户和角色是多对多---》中间表:auth_user_groups
   
   -django的auth多写了一个表,用户和权限的多对多:auth_user_user_permissions
   
# djago有个admin后台管理,配合auth,可以快速搭建出一个基于rbac的后台管理系统  

# 几年前,好多公司内部的管理系统,就是使用django的admin快速搭建的
# admin不好看,第三方美化
-django 1.x上很火的 Xadmin:前端基于jq+bootstrap,2.x上支持不好了,3.x直接不能用了,而且作者也弃坑了,他又做了一个前后端分离的admin
   -simple-ui:djanog 3.x

 

 

5 simple-ui的使用(对admin美化)

1 pip3 install django-simpleui
2 自定义左侧导航栏
import time
SIMPLEUI_CONFIG = {
   'system_keep': False,
   'menu_display': ['Simpleui','权限认证', '多级菜单测试','动态菜单测试'],      # 开启排序和过滤功能, 不填此字段为默认排序和全部显示, 空列表[] 为全部不显示.
   'dynamic': True,    # 设置是否开启动态菜单, 默认为False. 如果开启, 则会在每次用户登陆时动态展示菜单内容
   'menus': [
      {
       'name': 'Simpleui',
       'icon': 'fas fa-code',
       'url': '/home/' # 自己的地址(前后端混合项目)
  },
      {
       'app': 'auth',
       'name': '权限认证',
       'icon': 'fas fa-user-shield',
       'models': [{
           'name': '用户',
           'icon': 'fa fa-user',
           'url': 'auth/user/'
      }]
  },
      {
       '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'
      }]
  }]
}




## 智慧大屏
https://gitee.com/kevin_chou/dataVIS

 

 

 

作业

1 自定义user表,签发token和认证
2 使用simple-ui,修改一些样式,对着文档学习一下

 

 

上一篇:Keepalived 主备


下一篇:su 命令报错 su: Permission denied