drf JWT补充、jwt登录返回自定义接口,基于权限的角色控制(RBAC)、django缓存
一:jwt
1.控制用户登录后才能访问,和不登录就能访问
2.控制登录接口返回的数据格式
3.自定义基于jwt的权限类
4.手动签发token(多方式登录)
5.jwt的配置参数(过期时间)
二:基于角色的权限控制(django内置auth体系 RBAC)
三:django缓存(from django.core.cache import cache)
一:jwt
1.控制用户登录后才能访问,和不登录就能访问
# 1 控制用户登录后才能访问,和不登录就能访问
from rest_framework.permissions import IsAuthenticated
classOrderAPIView(APIView):# 登录才能
authentication_classes = [JSONWebTokenAuthentication,]
# 权限控制
permission_classes = [IsAuthenticated,]
defget(self,request,*args,**kwargs):
return Response('这是订单信息')
classUserInfoAPIView(APIView):# 不登录就可以
authentication_classes = [JSONWebTokenAuthentication,]
# 权限控制
# permission_classes = [IsAuthenticated,]
defget(self,request,*args,**kwargs):
return Response('UserInfoAPIView')
JWT token(内置)
2.控制登录接口返回的数据格式
# 2 控制登录接口返回的数据格式
-第一种方案,自己写登录接口
-第二种写法,用内置,控制登录接口返回的数据格式
-jwt的配置信息中有这个属性
'JWT_RESPONSE_PAYLOAD_HANDLER':
'rest_framework_jwt.utils.jwt_response_payload_handler',
-重写jwt_response_payload_handler,配置成咱们自己的
#settings 配置 JWT_AUTH={ 'JWT_RESPONSE_PAYLOAD_HANDLER':'app01.authjwt.My_jwt_response_payload_handler' }
#返回数据自定义重写 # 'JWT_RESPONSE_PAYLOAD_HANDLER':'rest_framework_jwt.utils.jwt_response_payload_handler', jwt内置返回配置 from rest_framework_jwt.utils import jwt_decode_handler #看源码重写
def My_jwt_response_payload_handler(token, user=None, request=None): return { 'token': token, 'status':100, 'msg':'成功', 'username':user.username }
返回格式修改
3.自定义基于jwt的权限类
# 3 自定义基于jwt的权限类
from rest_framework.authentication import BaseAuthentication # 基于它
from rest_framework_jwt.authentication import BaseJSONWebTokenAuthentication # 基于它
from rest_framework.exceptions import AuthenticationFailed
# from rest_framework_jwt.authentication import jwt_decode_handler
from rest_framework_jwt.utils import jwt_decode_handler # 跟上面是一个
import jwt
from api import models
# class MyJwtAuthentication(BaseAuthentication):
# def authenticate(self, request):
# jwt_value=request.META.get('HTTP_AUTHORIZATION')
# if jwt_value:
# try:
# #jwt提供了通过三段token,取出payload的方法,并且有校验功能
# payload=jwt_decode_handler(jwt_value)
# except jwt.ExpiredSignature:
# raise AuthenticationFailed('签名过期')
# except jwt.InvalidTokenError:
# raise AuthenticationFailed('用户非法')
# except Exception as e:
# # 所有异常都会走到这
# raise AuthenticationFailed(str(e))
# # 因为payload就是用户信息的字典
# print(payload)
# # return payload, jwt_value
# # 需要得到user对象,
# # 第一种,去数据库查
# # user=models.User.objects.get(pk=payload.get('user_id'))
# # 第二种不查库
# user=models.User(id=payload.get('user_id'),username=payload.get('username'))
# return user,jwt_value
# # 没有值,直接抛异常
# raise AuthenticationFailed('您没有携带认证信息')
classMyJwtAuthentication(BaseJSONWebTokenAuthentication):
defauthenticate(self, request):
jwt_value=request.META.get('HTTP_AUTHORIZATION')
if jwt_value:
try:
#jwt提供了通过三段token,取出payload的方法,并且有校验功能
payload=jwt_decode_handler(jwt_value)
except jwt.ExpiredSignature:
raise AuthenticationFailed('签名过期')
except jwt.InvalidTokenError:
raise AuthenticationFailed('用户非法')
except Exception as e:
# 所有异常都会走到这
raise AuthenticationFailed(str(e))
user=self.authenticate_credentials(payload) #提供了这个方法少去查数据库
return user,jwt_value
# 没有值,直接抛异常
raise AuthenticationFailed('您没有携带认证信息')
不需要JWT空格什么的
4.手动签发token(多方式登录)
eg(小知识点):login_ser = ser.LoginModelSerializer(data=request.data,context={'request':request}) context传入到序列化器中进行使用(序列话器点contexr就可以拿到字典)专业用于传递数据的序列化器和view的传输桥梁
# 使用用户名,手机号,邮箱,都可以登录#
# 前端需要传的数据格式
{
"username":"lqz/1332323223/33@qq.com",
"password":"lqz12345"
}
# 视图
from rest_framework.views import APIView
from rest_framework.viewsets import ViewSetMixin, ViewSet
from app02 import ser
classLogin2View(ViewSet): # 跟上面完全一样
deflogin(self, request, *args, **kwargs):
# 1 需要 有个序列化的类 要加data不然传给了instance(修改数据才需要的)查询只需要data
login_ser = ser.LoginModelSerializer(data=request.data,context={'request':request}) context传入到序列化器中进行使用(序列话器点contexr就可以拿到字典)
# 2 生成序列化类对象
# 3 调用序列号对象的is_validad
login_ser.is_valid(raise_exception=True)
token=login_ser.context.get('token')
# 4 return
return Response({'status':100,'msg':'登录成功','token':token,'username':login_ser.context.get('username')})
# 序列化类
from rest_framework import serializers
from api import models
import re
from rest_framework.exceptions import ValidationError
from rest_framework_jwt.utils import jwt_encode_handler,jwt_payload_handler
classLoginModelSerializer(serializers.ModelSerializer):
username=serializers.CharField() # 重新覆盖username字段,数据中它是unique,post,认为你保存数据,自己有校验没过
classMeta:
model=models.User
fields=['username','password']
defvalidate(self, attrs):
print(self.context) 视图传过来的request
# 在这写逻辑
username=attrs.get('username') # 用户名有三种方式
password=attrs.get('password')
# 通过判断,username数据不同,查询字段不一样
# 正则匹配,如果是手机号
if re.match('^1[3-9][0-9]{9}$',username):
user=models.User.objects.filter(mobile=username).first()
elif re.match('^.+@.+$',username):# 邮箱
user=models.User.objects.filter(email=username).first()
else:
user=models.User.objects.filter(username=username).first()
if user: # 存在用户
# 校验密码,因为是密文,要用check_password
if user.check_password(password):
# 签发token
payload = jwt_payload_handler(user) # 把user传入,得到payload
token = jwt_encode_handler(payload) # 把payload传入,得到token
self.context['token']=token 视图传过来的context 添加数据到context可以在视图层拿到 类似于桥梁
self.context['username']=user.username
return attrs
else:
raise ValidationError('密码错误')
else:
raise ValidationError('用户不存在')
发送的是post请求
5.jwt的配置参数(过期时间)
# jwt的配置
import datetime
JWT_AUTH={
'JWT_RESPONSE_PAYLOAD_HANDLER':'app02.utils.my_jwt_response_payload_handler',
'JWT_EXPIRATION_DELTA': datetime.timedelta(days=7), # 过期时间,手动配置
}
二:基于角色的权限控制(django内置auth体系 RBAC)
# RBAC :是基于角色的访问控制(Role-Based Access Control ),公司内部系统
# django的auth就是内置了一套基于RBAC的权限系统
# django中
# 后台的权限控制(公司内部系统,crm,erp,协同平台)
user表
permssion表
group表
user_groups表是user和group的中间表
group_permissions表是group和permssion中间表
user_user_permissions表是user和permission中间表
# 前台(主站),需要用三大认证
# 演示:
三:django缓存
# 前端混合开发缓存的使用 -缓存的位置,通过配置文件来操作(以文件为例) CACHES = { 'default': { 'BACKEND': 'django.core.cache.backends.filebased.FileBasedCache', #指定缓存使用的引擎 'LOCATION': 'D:\django_cache', #指定缓存的路径(自己先创建好) 'TIMEOUT':300, #缓存超时时间(默认为300秒,None表示永不过期) 'OPTIONS':{ 'MAX_ENTRIES': 300, # 最大缓存记录的数量(默认300) 'CULL_FREQUENCY': 3, # 缓存到达最大个数之后,剔除缓存个数的比例,即:1/CULL_FREQUENCY(默认3) } } } -缓存的粒度: -------------------------------------------------------------------------------------------------------------- -全站缓存(一般不用) 中间件 MIDDLEWARE = [ 'django.middleware.cache.UpdateCacheMiddleware', 位置一定不能乱(里面写了process_response) 。。。。 'django.middleware.cache.FetchFromCacheMiddleware', (里面写了process_request) ] CACHE_MIDDLEWARE_SECONDS=10 # 全站缓存时间 -------------------------------------------------------------------------------------------------------- -单页面缓存 在视图函数上加装饰器 from django.views.decorators.cache import cache_page @cache_page(5) # 缓存5s钟 deftest_cache(request): import time ctime=time.time() return render(request,'index.html',context={'ctime':ctime}) --------------------------------------------------------------------------------------------------------- -页面局部缓存 {% load cache %} {% cache 5 'name' %} # 5表示5s钟,name是唯一key值 {{ ctime }} {% endcache %} ----------------------------------------------------------------------------------------------------------- # 前后端分离缓存的使用(重点) - 如何使用 from django.core.cache import cache cache.set('key',value可以是任意数据类型,过期多少秒清除180秒是3分钟) cache.get('key') 取出 -应用场景: -第一次查询所有图书,你通过多表联查序列化之后的数据,直接缓存起来 -后续,直接先去缓存查,如果有直接返回,没有,再去连表查,返回之前再缓存
前后端分离缓存图解

四:补充
1.补充base64使用
# base64编码和解码
#md5固定长度,不可反解
#base63 变长,可反解
#编码(字符串,json格式字符串)
import base64
import json
dic={'name':'lqz','age':18,'sex':'男'}
dic_str=json.dumps(dic)
ret=base64.b64encode(dic_str.encode('utf-8'))
print(ret)
# 解码
# ret是带解码的串
ret2=base64.b64decode(ret)
print(ret2)