1. 程式人生 > >drf☞jwt自動簽發與手動簽發

drf☞jwt自動簽發與手動簽發

[TOC] ## 一、自動簽發 **urls** ```python from rest_framework_jwt.views import obtain_jwt_token # 使用jwt自帶的登入檢視 urlpatterns = [ path('login/', obtain_jwt_token), ] ``` **settings** ```python import datetime JWT_AUTH={ # 配置響應格式,必須和自動簽發使用 'JWT_RESPONSE_PAYLOAD_HANDLER':'app01.utils.my_jwt_response_payload_handler', 'JWT_EXPIRATION_DELTA': datetime.timedelta(days=7), # 過期時間,手動配置 } ``` **utils** ```python # 重寫jwt響應格式(需要到settings配置) # 與之配合使用的必須是自動簽發 def my_jwt_response_payload_handler(token, user=None, request=None): # 返回什麼,前端就能看到什麼樣子 return { 'token': token, 'msg':'登入成功', 'status':100, 'username':user.username } ``` 然後直接在前端提交post請求傳送賬號和密碼,會返回我們定義好的響應格式 ```python { "token": "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ1c2VyX2lkIjoxLCJ1c2VybmFtZSI6Imh6IiwiZXhwIjoxNTk1NDE1MTEyLCJlbWFpbCI6IiJ9.BxBdsm6oBz8iPAwSSpo_7IaU4pBp6RjK4c0GJ_FYN1E", "msg": "登入成功", "status": 100, "username": "hz" } ``` 然後拿出token對測試類傳送測試請求 ```python class TestAPI(APIView): def get(self,request): print(request.user) return Response("ok") # 因為內建的他沒有對匿名使用者設定攔截,素以匿名使用者也能看到ok # 我們用request.user來區分 # 這裡可能會出現我登入了很多次,用每次不同的token都能登入 # 這是因為token校驗的是規則,是要加密規則符合且沒有超時,那用哪次token都一樣的 ``` ## 二、手動簽發 **utils** ```python from rest_framework_jwt.authentication import BaseJSONWebTokenAuthentication,jwt_decode_handler import jwt from rest_framework.exceptions import AuthenticationFailed class MyAuthentication(BaseJSONWebTokenAuthentication): # 這裡重寫的邏輯和BaseJSONWebTokenAuthentication裡的authenticate一模一樣 def authenticate(self, request): jwt_token = request.META.get('HTTP_AUTHORIZATION') # 獲取瀏覽器傳來的token if jwt_token: try: payload = jwt_decode_handler(jwt_token) # 傳入token,拿出第二段使用者資訊,有內建的校驗token功能 except jwt.ExpiredSignature: raise AuthenticationFailed('簽名過期') except jwt.InvalidTokenError: raise AuthenticationFailed('使用者非法') except Exception as e: # 所有異常都會走到這 raise AuthenticationFailed(str(e)) # 通過內建的方法把payload轉換成使用者物件 user = self.authenticate_credentials(payload) return user,None # ===》request.user,request.auth raise AuthenticationFailed('您沒有攜帶認證資訊') ``` **sers** ```python from rest_framework import serializers # 多方序列化校驗登入 import re from rest_framework.exceptions import ValidationError from app01 import models from rest_framework_jwt.utils import jwt_encode_handler,jwt_payload_handler class LoginSer(serializers.ModelSerializer): # 我們要提交校驗資料的時候,如果直接用下面Meta繫結給模型類的話 # 關鍵點2:這裡如果不寫username的話,序列化器直接用的是模型類的username # 這兩者的區別在於,如果覆蓋寫了username,他表示的可以是任何前端傳來的資料,如果是模型類繫結,那隻能是使用者名稱了 # 我們這裡username用於多方登入的校驗資料,必須要重寫 # 而password不用重寫,因為password用的就是模型類本身的 username = serializers.CharField() class Meta: model = models.User fields =['username','password'] def validate(self, attrs): username = attrs.get('username') password = attrs.get('password') if 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: if user.check_password(password): # 關鍵點3:jwt_payload_handler把使用者資料物件轉化成使用者資訊的字典 # jwt_encode_handler把使用者資訊的字典轉化成token payload = jwt_payload_handler(user) # print('user:',user,type(user)) token = jwt_encode_handler(payload) # print('payload:',payload,type(payload)) # print('token:',token) # 關鍵點4:如果我們要給序列化器新增資料,讓檢視函式去使用 # 通常都是傳給物件的context屬性,當然直接賦值也可以,這只是他給我們提供的傳值介面 self.context['token'] = token self.context['user'] = user self.user = user return attrs else: raise ValidationError('密碼錯誤') raise ValidationError('不存在使用者') raise ValidationError('請輸入使用者名稱') ``` **views** ```python class LoginApi(ViewSet): authentication_classes = [] def login(self, request): # 在呼叫序列化類給context傳資料,可以直接在序列化類中呼叫 # 關鍵點1:注意區分序列化傳值與反序列化 # 這裡只要拿字典取校驗資料,那就傳給data # 如果是要把資料物件轉化成字典就傳給instance user_ser = sers.LoginSer(data=request.data, context={'request': request}) user_ser.is_valid(raise_exception=True) token = user_ser.context.get('token') user = user_ser.context.get('user') print(user_ser.user) return Response({'code': 100, 'msg': '登入成功', 'token': token, 'username': user.usernam