JWT LL

朱饱饱發表於2024-03-13

1、工作原理

""" 
1) jwt = base64(頭部).base(載荷  payload).hash256(base64(頭部).base(載荷).金鑰)
2) base64是可逆的演算法、hash256是不可逆的演算法
3) 金鑰是固定的字串,儲存在伺服器
"""

2、drf-jwt

官網
http://getblimp.github.io/django-rest-framework-jwt/

安裝子:虛擬環境
pip install djangorestframework-jwt

使用:user/urls.py
from django.urls import path
from rest_framework_jwt.views import obtain_jwt_token
urlpatterns = [
    path('login/', obtain_jwt_token),
]

測試介面:post請求
"""
postman發生post請求

介面:http://api.luffy.cn:8000/user/login/

資料:
{
    "username":"admin",
    "password":"admin"
}
"""

3、drf-jwt開發

配置資訊:JWT_AUTH到dev.py中
import datetime
JWT_AUTH = {
    # 過期時間
    'JWT_EXPIRATION_DELTA': datetime.timedelta(days=1),
    # 自定義認證結果:見下方序列化user和自定義response
    'JWT_RESPONSE_PAYLOAD_HANDLER': 'user.utils.jwt_response_payload_handler',  
}

序列化user:user/serializers.py(自己建立)
from rest_framework import serializers
from . import models
class UserModelSerializers(serializers.ModelSerializer):
    class Meta:
        model = models.User
        fields = ['username']

自定義response:user/utils.py
from .serializers import UserModelSerializers
def jwt_response_payload_handler(token, user=None, request=None):
    return {
        'status': 0,
        'msg': 'ok',
        'data': {
            'token': token,
            'user': UserModelSerializers(user).data
        }
    }

基於drf-jwt的全域性認證:user/authentications.py(自己建立)
import jwt
from rest_framework.exceptions import AuthenticationFailed
from rest_framework_jwt.authentication import jwt_decode_handler
from rest_framework_jwt.authentication import get_authorization_header
from rest_framework_jwt.authentication import BaseJSONWebTokenAuthentication

class JSONWebTokenAuthentication(BaseJSONWebTokenAuthentication):
    def authenticate(self, request):
        jwt_value = get_authorization_header(request)

        if not jwt_value:
            raise AuthenticationFailed('Authorization 欄位是必須的')
        try:
            payload = jwt_decode_handler(jwt_value)
        except jwt.ExpiredSignature:
            raise AuthenticationFailed('簽名過期')
        except jwt.InvalidTokenError:
            raise AuthenticationFailed('非法使用者')
        user = self.authenticate_credentials(payload)

        return user, jwt_value

 解讀:

JSONWebTokenAuthentication 是一個自定義身份驗證類,它通常用於 Django REST framework 中實現
基於 JSON Web Token (JWT) 的使用者認證。
這個類繼承自 BaseJSONWebTokenAuthentication,並覆蓋了 authenticate 方法以處理 JWT 的驗證邏輯。 在該方法中: 透過 get_authorization_header(request) 函式從請求的頭部(通常是
'Authorization' 欄位)獲取 JWT 值。 如果沒有找到 JWT 值,則丟擲 AuthenticationFailed 異常,提示 "Authorization 欄位是必須的"。 嘗試使用 jwt_decode_handler(jwt_value) 解碼提供的 JWT。這個解碼函式會檢查簽名的有效性和 token 是否過期等。 如果簽名已過期,會丟擲 ExpiredSignature 異常,此時捕獲異常並丟擲 AuthenticationFailed,提示 "簽名過期"。 如果 JWT 校驗失敗(比如 token 格式錯誤、金鑰不匹配或其他任何無效情況),會丟擲 InvalidTokenError 異常,
此時也會捕獲異常並丟擲 AuthenticationFailed,提示
"非法使用者"。 如果 JWT 解碼成功,呼叫 self.authenticate_credentials(payload) 來進一步驗證 payload 中攜帶的使用者憑證資訊是否有效,
並據此獲取或建立對應的使用者物件。 如果使用者憑證驗證成功,返回一個包含使用者物件和原始 JWT 值的元組 (user, jwt_value)。這樣,經過此身份驗證流程後,
合法的使用者就能透過 JWT 進行後續的 API 請求授權訪問。

全域性啟用:settings/dev.py
REST_FRAMEWORK = {
    # 認證模組
    'DEFAULT_AUTHENTICATION_CLASSES': (
        'user.authentications.JSONWebTokenAuthentication',
    ),
}

區域性啟用禁用:任何一個cbv類首行
# 區域性禁用
authentication_classes = []

# 區域性啟用
from user.authentications import JSONWebTokenAuthentication
authentication_classes = [JSONWebTokenAuthentication]

多方式登入:user/utils.py
import re
from .models import User
from django.contrib.auth.backends import ModelBackend
class JWTModelBackend(ModelBackend):
    def authenticate(self, request, username=None, password=None, **kwargs):
        try:
            if re.match(r'^1[3-9]\d{9}$', username):
                user = User.objects.get(mobile=username)
            else:
                user = User.objects.get(username=username)
        except User.DoesNotExist:
            return None
        if user.check_password(password) and self.user_can_authenticate(user):
            return user

配置多方式登入:settings/dev.py
AUTHENTICATION_BACKENDS = ['user.utils.JWTModelBackend']

手動簽發JWT:瞭解 - 可以擁有原生登入基於Model類user物件簽發JWT
from rest_framework_jwt.settings import api_settings

jwt_payload_handler = api_settings.JWT_PAYLOAD_HANDLER
jwt_encode_handler = api_settings.JWT_ENCODE_HANDLER

payload = jwt_payload_handler(user)
token = jwt_encode_handler(payload)