DRF之JWT补充
1.JWT控制用户登录后才能反问,匿名用户无法访问
class QueryUserView(GenericViewSet, RetrieveModelMixin):
"""
查询接口
"""
queryset = User.objects.all()
serializer_class = UserSerializer
pk = None
# throttle_classes = [IPThrottle, ]
authentication_classes = [LoginToken, ]
permission_classes = [IsAuthenticated, ]
def get_object(self):
queryset = self.filter_queryset(self.get_queryset())
filter_kwargs = {self.lookup_field: self.pk}
return queryset.filter(**filter_kwargs)
def get_queryset(self):
queryset = self.queryset
if isinstance(queryset, QuerySet):
queryset = queryset.all()
return queryset.filter(is_delete=False)
def retrieve(self, request, *args, **kwargs):
self.pk = kwargs.get('pk')
if not self.pk:
return APIResponse(code='102', msg='查询失败', data={'result': '缺少主键值'})
if not self.get_object():
return APIResponse(code='102', msg='查询失败', data={'result': '无效的主键值'})
instance = self.get_object().first()
serializer = self.get_serializer(instance=instance)
return APIResponse(code='102', msg='查询成功', data=serializer.data)
def query(self, request, *args, **kwargs):
return self.retrieve(request, *args, **kwargs)
不携带token或者token失效无法登录成功
不携带token或者token失效,系统会默认你为匿名用户
先进行登录,获取token值。携带正确的token可以登录成功
将权限校验去掉后,即使携带正确的token也登录失败,因为系统此时默认你为匿名用户
2.控制登录接口返回数据的格式
方案一:自己写登录接口
方案二:用内置控制登录接口返回数据的格式
JWT配置信息中有这个属性
JWT_AUTH = {
'JWT_RESPONSE_PAYLOAD_HANDLER':
'rest_framework_jwt.utils.jwt_response_payload_handler',
}
# 重写jwt_response_payload_handler,然后进行替换
自定义基于JWT的权限认证
# 自定义基于jwt的权限认证
from rest_framework.authentication import BaseAuthentication
from rest_framework_jwt.authentication import BaseJSONWebTokenAuthentication
from rest_framework.exceptions import AuthenticationFailed
from rest_framework_jwt.utils import jwt_decode_handler
from rest_framework_jwt.utils import jwt_encode_handler
import jwt
from app01 import models
class XJwtAuthentication(BaseJSONWebTokenAuthentication):
def authenticate(self, request):
jwt_value = str(request.META.get('HTTP_AUTHORIZATION')) # 获取token信息
if jwt_value:
try:
payload = jwt_decode_handler(jwt_value) # 取出payload
except jwt.ExpiredSignatureError:
raise AuthenticationFailed('签名过期')
except jwt.InvalidTokenError:
raise AuthenticationFailed('非法用户')
except Exception as e:
raise AuthenticationFailed(str(e))
"""
得到user对象:
第一种方法去数据库查,
第二种不查数据库
"""
# user = models.User.object.get(pk=payload.get('user_id')) 第一种
user = models.User(id=payload.get('user_id'),username=payload.get('username'))
return user,jwt_value
else:
raise AuthenticationFailed('没有携带token认证信息')
jwt提供了通过三段token,取出payload的方法,并且有校验功能
class XJwtAuthentication(BaseJSONWebTokenAuthentication):
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))
user=self.authenticate_credentials(payload)
return user,jwt_value
# 没有值,直接抛异常
raise AuthenticationFailed('您没有携带认证信息')
3.手动签发token来实现多方式的登录
# 使用用户名/手机号/邮箱都可以登录
# 前端需要传回来的json格式数据
{
"username":"surpass/18395806407/18395806407@163.com"
}
LoginSerializer
from rest_framework.exceptions import ValidationError
from rest_framework_jwt.utils import jwt_encode_handler, jwt_payload_handler
import re
class LoginSerializer(serializers.ModelSerializer):
username = serializers.CharField(max_length=32) # 覆盖username
class Meta:
model = models.User
fields = ['username', 'password']
def validate(self, attrs):
username = attrs.get('username')
password = attrs.get('password')
if re.match('^1[3|4|5|7|8][0-9]{9}$', username):
user = models.User.objects.filter(mobile=username)
elif re.match('^.+@.+$', username):
user = models.User.objects.filter(email=username)
else:
user = models.User.objects.filter(username=username)
if not user:
raise ValidationError('用户不存在')
if not user.check_password(password):
raise ValidationError('密码错误')
payload = jwt_payload_handler(user) # 传入user得到payload
token = jwt_encode_handler(payload) # 传入payload得到token 即jwt_value
self.context['token'] = token # 利用context返回token和user
self.context['user'] = user
return attrs
LoginView
class LoginView(GenericViewSet):
queryset = User.objects.all()
serializer_class = LoginSerializer
def login(self, request, *args, **kwargs):
login_ser = self.get_serializer(data=request.data, context={})
login_ser.is_valid(raise_exception=True)
token = login_ser.context.get('token')
user = login_ser.context.get('user')
return APIResponse(code=100, msg='登录成功', data={'token': token, 'username': user.username})
JWT的配置参数
# 使用内置的obtain_jwt_token时使用
JWT_AUTH = {
'JWT_RESPONSE_PAYLOAD_HANDLER': 'app01.utils.jwt_response_payload_handler',
'JWT_EXPIRATION_DELTA': datetime.timedelta(days=7), # 过期时间,手动配置
}
# utils.py
def jwt_response_payload_handler(token, user=None, request=None):
return {
'token': token,
'msg': '登录成功',
'status': 100,
'username': user.username
}
4.基于角色的权限控制(django内置的auth模块)
RBAC(Role-Based Access Control):基于角色的访问控制,通常用于公司的内部系统
# django的auth就是内置了一套关于RBAC的权限系统
核心是:通过角色绑定权限,然后给用户分配角色
在这种访问机制中,引入了role的概念,将用户与权限之间的关系进行了解耦,让用户通过角色与权限进行关联。在RBAC模型中,(who,what,how)构成了访问权限的三元组。
# 在django中
# 后台的权限控制(公司内部系统,crm,erp,协同平台)
user表
permssion表
group表
user_groups表是user和group的中间表
group_permissions表是group和permssion中间表
user_user_permissions表是user和permission中间表
# 前台(主站),需要用三大认证
5 .Django缓存
- 前后端混合开发缓存的位置
# 缓存的位置,通过配置文件来操作(以文件为例子)
# 缓存的粒度:全站缓存,单页面缓存,以及页面局部缓存
页面局部缓存:
<body>
<div>
{{ t }}<hr>
{% load cache %}
{% cache 5 'name' %}
{{ time }}
{% endcache %}
</div>
</body>
单页面缓存:
# 给视图函数加上装饰器
from django.views.decorators.cache import cache_page
@cache_page(3) # 缓存10秒中
def test2(request):
import time
time = time.time
back_dict = {'time': time}
return render(request, 'test2.html', back_dict)
全站缓存:
# 在中间件设置
MIDDLEWARE = [
'django.middleware.cache.UpdateCacheMiddleware',
...
'django.middleware.cache.FetchFromCacheMiddleware',
]
CACHE_MIDDLEWARE_SECONDS=10 # 全站缓存时间
-
前后端分离项目缓存的使用
# 使用步骤: # views.py from django.core.cache import cache def test1(request): import datetime cache.set('time',datetime.datetime.now()) return render(request, 'test1.html') def test2(request): time = cache.get('time') back_dict = {'time':time} return render(request, 'test2.html', back_dict) # 应用场景 第一次查询所有图书,你通过多表联查序列化之后的数据,直接缓存起来 后续,直接先去缓存查,如果有直接返回,没有,再去连表查,返回之前再缓存