1、过滤的源码分析
-视图类中配置类属性:filter_backends = ["过滤类"]
-必须继承ListAPIView(或ListModelMixin+GenericAPIView,因为ListAPIView=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 djangorestframework-jwt快速使用
-快速签发
-在路由中(登录功能有了,基于auth的user表实现的登录)
path('login/', obtain_jwt_token)
-快速认证
-在视图类中配置
authentication_classes = [JSONWebTokenAuthentication, ] #drf_jwt
permission_classes = [IsAuthenticated] # drf
还有一点:
实现国际化,除了要将配置文件改了,还要将第三方的APP注册一下,才能实现
jwt内置签发方法修改返回格式
现在用户一登录,只返回了token,但是实际情况下,需要返回好些用户信息
实现:
1、写一个函数
2、在配置文件中配置
上面函数就写好了,下面需要配置一下
读一下源码,为啥我们这样写,就能生效
源码(from rest_framework_jwt import settings)这里就写了一个token
jwt签发的源码分析
路由的====> obtain_jwt_token(from rest_framework_jwt.views import obtain_jwt_token)=====>
obtain_jwt_token = ObtainJSONWebToken.as_view()====>视图类
class ObtainJSONWebToken(JSONWebTokenAPIView):
#序列化类
serializer_class = JSONWebTokenSerializer
登录请求,发了一个post请求,携带用户名和密码====>所以ObtainJSONWebToken的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源码
def validate(self, attrs):
credentials = {
self.username_field: attrs.get(self.username_field),
'password': attrs.get('password')
} # 取出前端传过来的数据{“username”:“lqz”,“password”:“lqz123456”}
if all(credentials.values()):
user = authenticate(**credentials) # 通过用户名密码认证auth的user表中的用户
if user:
if not user.is_active:
# 通过user得到荷载{user_id:1,username:lqz,...}
payload = jwt_payload_handler(user)
return {
'token': jwt_encode_handler(payload), #通过荷载得到token串
'user': user
}
token认证的源码分析
写一个视图类
上面的写好之后,访问这个接口就需要携带着token过来了,并且携带token的方式要放在请求头中,并且是jwt空格token值(都是固定格式)
下面我们就去源码里面看看,为啥要这样
1、读认证类JSONWebTokenAuthentication的父类BaseJSONWebTokenAuthentication的authenticate方法
class BaseJSONWebTokenAuthentication(BaseAuthentication):
def authenticate(self, request):
# 取出前端传入的Authorization的value值,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去user表中获取当前用户
user = self.authenticate_credentials(payload)
return (user, jwt_value)
2、JSONWebTokenAuthentication的get_jwt_value方法,返回token串
class JSONWebTokenAuthentication(BaseJSONWebTokenAuthentication):
www_authenticate_realm = 'api'
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:# 元祖的长度等于1,抛异常,因为我们jwt token串
msg = _('Invalid Authorization header. No credentials provided.')
raise exceptions.AuthenticationFailed(msg)
elif len(auth) > 2: #元祖的长度大于2,抛异常,因为我们jwt token串,应该是等于2
msg = _('Invalid Authorization header. Credentials string '
'should not contain spaces.')
raise exceptions.AuthenticationFailed(msg)
return auth[1]
读完上面的源码,我们就可以自定义签发和认证token了。签发就是自己写一个登录,等过用户等到payload,通过payload得到token
验证的话,就是写一个认证类重写authenticate方法
自定义user表实现jwt的token签发
代码:
###########使用自己的用户表,签发token
from .models import UserInfo
from rest_framework.response import Response
from rest_framework_jwt.settings import api_settings # 源码里面
jwt_payload_handle = 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: # 用户名和密码正确
payload = jwt_payload_handle(user) # 源码里面
token = jwt_encode_handler(payload) # 源码里面
return Response({"code": 100, "msg": "登录成功", "token": token, "username": user.username})
else:
return Response({"code": 101, "msg": "用户名或密码错误"})
自定义user表实现jwt的token认证
上面完成了签发,下面就是需要认证了,就是写一个认证类:重写authenticate方法
下面在配置一下认证类
补充一点:
上面每次访问接口,都需要去数据库,拿当前用户。这样不好
这是一种方案
另一种方案
上面就是稍微优化一下,不是每次都去查询当前登录用户(源码中确实每次都会去查)
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的rbac的表就结束了,但是
django的auth多写了一个表,用户和权限的多对多(为了更细粒度的管理权限):auth_user_user_permissions
所以一共6张表
18
django有个admin后台管理,配合auth,可以快速搭建出一个基于rbac的后台管理系统
现在是超级用户登录的,他有真删改查的权限,我们现在再新增一个用户,让他只有查看的权限
增加用户,只能超级用户增
现在这个用户对book表只有查看权限
几年前,好多公司内部的管理系统,就是使用django的admin快速搭建的
admin不好看,第三方美化
-django 1.x上很火的 Xadmin:前端基于jq+bootstrap,2.x上支持不好了,3.x直接不能用了,而且作者也弃坑了,他又做了一个前后端分离的admin
-simple-ui:djanog 3.x
我们现在需要将我们自己的django_admin 美化一下
1、装一下:pip install django-simpleui
2、把simpleui注册在第一行
官网:https://simpleui.72wo.com/docs/simpleui/doc.html
这里首页也可以显示内网的地址
但是这样还是不好看,可以搞一些大屏,直线图
这就需要集成echars(号称5分钟上手,hichars号称1分钟上手)
echars(https://echarts.apache.org/zh/index.html)原来是百度做的,后来给了Apache基金会,成了Apache顶级的开源项目(kafka也是Apache项目)
copy这里:https://echarts.apache.org/handbook/zh/get-started/