DRF之JWT補充
阿新 • • 發佈:2020-07-15
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/[email protected]"
}
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) # 應用場景 第一次查詢所有圖書,你通過多表聯查序列化之後的資料,直接快取起來 後續,直接先去快取查,如果有直接返回,沒有,再去連表查,返回之前再快取