1. 程式人生 > 實用技巧 >drf之jwt傻瓜式啟動說明書

drf之jwt傻瓜式啟動說明書

DRF之jwt使用說明書

jwt介紹

JsonWebToken

如何獲取token

先建立一張使用者表,要想使用jwt,必須使用django自帶的使用者表

  • 在models.py中寫一個使用者類,匯入、繼承AbstractUser

  • 在Terminal中做資料庫遷移,createsuperuser建立超級使用者

  • 在路由中匯入jwt的模組

    # urls.py
    from django.urls import path, include,re_path
    from rest_framework_jwt.views import obtain_jwt_token
    from api import views
    
    urlpatterns = [
        path('loginView/', views.Login.as_view()),
        re_path(r"^login/", obtain_jwt_token)
        # login觸發jwt的檢視函式
    ]
    
  • 向login傳送post請求,請求體中加上username,password(這取決於使用者表中的欄位名),只需要且只能用這兩個來認證

  • 得到響應,響應中就是token

這裡就得到了token,這個token有預設的過期時間,把它放在請求頭可以用來做登陸認證

自定義認證

前端完成登陸,獲取了token,放在請求頭中傳送給後端,後端接收並進行驗證

如果使用jwt內建的認證方法,預設會把你header裡token分開為兩段,第一段是 JWT 第二段是token,jwt會拿第二段去認證

注:登入介面需要做 認證 + 許可權 兩個區域性禁用

# auth.py
from rest_framework_jwt.authentication import BaseJSONWebTokenAuthentication
from rest_framework_jwt.authentication import jwt_decode_handler
from rest_framework import exceptions

class MyToken(BaseJSONWebTokenAuthentication):
    # 繼承jwt的方法,重寫authenticate
    def authenticate(self, request):
        jwt_value = str(request.META.get('HTTP_AUTHORIZATION'))
        # 從請求頭中取出token:http會自動把前端的authorization變成大寫,加上HTTP_字首
        try:
            payload = jwt_decode_handler(jwt_value)
            # 使用繼承的類中的方法取token中的payload,驗證
            print(payload)
        except Exception:
            raise exceptions.AuthenticationFailed("認證失敗")
        user = self.authenticate_credentials(payload)
        return user, None
    # 認證成功,返回使用者物件,這時就可以在檢視中任意位置取payload中給的資訊了
# views.py
from rest_framework.views import APIView
from rest_framework.response import Response
from utils.auth import MyToken

class Login(APIView):
    authentication_classes = [MyToken,]
    # 區域性使用,配置認證類。如果要全域性使用,去setting中配置
    def get(self,request):
        return Response("Login")

使用內建的認證

內建的jwt前端使用格式是這樣

訪問需要登陸認證後才能訪問的url時,在請求頭(Header)中新增 Authorization:JWT <your_token> (Authorization 為 name,JWT <your_token> 為 value)

後端使用jwt內建的認證方式,僅需匯入兩個模組配置一下就可以了

from rest_framework.views import APIView
from rest_framework.response import Response
from rest_framework_jwt.authentication import JSONWebTokenAuthentication
# 認證模組
from rest_framework.permissions import IsAuthenticated
# 許可權模組
from utils.auth import MyToken

class Login(APIView):
    authentication_classes = [JSONWebTokenAuthentication,]
    permission_classes = [IsAuthenticated]
    # 配置認證和許可權
    def get(self,request):
        return Response("Login")

注意,使用內建的認證方法,必須認證和許可權一起使用才能有效,只使用一個認證不用許可權,也可以訪問到介面

控制返回的資料格式

控制登陸介面返回的資料格式,讓obtain_jwt_token不止返回一個“token”,重寫整個登陸介面,或使用原有的登陸介面,改一些配置

第一種方法,改配置

在jwt的配置檔案中有個屬性

'JWT_RESPONSE_PAYLOAD_HANDLER':'rest_framework_jwt.utils.jwt_response_payload_handler',

控制返回資料的格式,我們只要重寫jwt_response_payload_handler的返回值,配置進去就可以控制返回的資料格式了

# setting.py 或者任意別的能先於jwt執行到的地方
from api.utils
JWT_AUTH = {
	'JWT_RESPONSE_PAYLOAD_HANDLER':'api.utils.my_jwt_response_payload_handler'
}
# utils.py
def my_jwt_response_payload_handler(token,user=None,request=None):
    return {
        'token':token,
        'status':200,
        'msg':'登陸成功'
    }

自定義基於jwt的認證類

兩種寫法

# auth.py
# 第一種,基於BaseAuthentication
from rest_framework.authentication import BaseAuthentication
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('您沒有攜帶認證資訊')

第二種

# 基於BaseJSONWebTokenAuthentication
from rest_framework_jwt.authentication import BaseJSONWebTokenAuthentication
class MyJwtAuthentication(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('您沒有攜帶認證資訊')

在檢視中使用authentication_classes = [MyJwtAuthentication,]匯入使用