DRF的JWT用戶認證


DRF的JWT用戶認證

從根本上來說,JWT是一種開放的標准(RFC 7519), 全稱為json web token ,其存在的意義在於,對於前后端分離的項目來說,后端不需要存儲token,主需要存儲簽發和校驗token的算法,所以我們需要在前端存儲token,然后通過JWT加密之后傳送給后端,從而完成校驗.

JWT算法認證的最大優點在於適合服務器集群部署,這樣對於比較大的項目可以從根本上提升並發量.

JWT的認證規則

JWT的格式

jwt的格式是三段式,即header(頭) . payload(負載) . Signature(簽名),其中頭和載負載采用的是base64可逆的加密,簽名采用的則是md5不可逆的加密,具體如下:

  • 頭(基礎信息): 包含兩部分,token類型以及采用的加密算法
  • 載負載(核心信息): 主要是用戶信息,過期時間等
  • 簽名(安全保證): 包括三方面,即頭加密結果+載荷加密結果+服務器秘鑰,采用的是md5加密

我們的后台一定要保證服務器秘鑰的安全,因為在jwt里面服務器秘鑰是唯一的安全保障

JWT認證的流程

后台簽發token -> 前台存儲 -> 前台想后台發送需要認證的請求且攜帶token -> 后台校驗token得到合法的用戶

JWT模塊的導入為

# 在cmd或者Terminal窗口中:
pip install djangorestframework-jwt

# 模塊包名為rest_framework_jwt

JWT的使用

JWT最常用的是三個接口,簽發token,校驗token以及刷新token,我們可以分別在urls.py里面導入

# /urls.py
from django.conf.urls import url
from . import views

from rest_framework_jwt.views import ObtainJSONWebToken, obtain_jwt_token, verify_jwt_token, refresh_jwt_token
urlpatterns = [
    # drf-jwt三個視圖接口
    url(r'^login/$', obtain_jwt_token),
    url(r'^verify/$', verify_jwt_token),
    url(r'^refresh/$', refresh_jwt_token),
]

# 導入之后,我們還需要在settings.py里面配置相關的JWT的配置項
# /settings.py

import datetime
JWT_AUTH = {
    # 設置JWT的過期時間
    'JWT_EXPIRATION_DELTA':datetime.timedelta(days=7),
    
    # 自定義jwt插件的配置
    'JWT_PEFRESH_EXPIRATION_DELTA':detetime.timedelta(days=7),
    
    # 設置JWT的頭
    'JWT_AUTH_HEADER_PREFIX':'JWT'
}

下面我們就用代碼來實現jwt的整個邏輯,大概邏輯如下:

  1. 視圖類中,將請求數據交給序列化完成校驗,然后返回用戶信息和對應token
  2. 序列化類,自定義反序列化的字段,利用全局鈎子校驗數據得到user和token,並且保存在序列化類對象中
  3. token可以采用jwt插件的rest_framework_jwt.serializers中jwt_payload_handler,jwt_encode_handler來完成簽發

下面我們做一個實例,該小例子實現了用戶的多方式登錄:

# urls.py
from django.conf.urls import url
from . import views

urlpatterns = [

    url(r'^login/$', views.LoginAPIView.as_view())
]

# serializers.py
from rest_framework.serializers import ModelSerializer, CharField, ValidationError, SerializerMethodField
from . import models
import re
from rest_framework_jwt.serializers import jwt_payload_handler, jwt_encode_handler
class LoginSerializer(ModelSerializer):
    # 定義用戶名和密碼兩個僅支持反序列化的字段
    username = CharField(write_only=True)
    password = CharField(write_only=True)
    class Meta:
        model = models.User
        fields = ('username', 'password')

    # 在全局鈎子中簽發token
    def validate(self, attrs):
        user = self._many_method_login(**attrs)
        # 將數據存放到序列化對象中
        payload = jwt_payload_handler(user)
        token = jwt_encode_handler(payload)

        self.user = user
        self.token = token

        return attrs

    # 多方式登錄
    def _many_method_login(self, **attrs):
        username = attrs.get('username')
        password = attrs.get('password')
        # 正則匹配,如果有@符號,則判定為郵箱登錄
        if re.match(r'.*@.*', username):
            user = models.User.objects.filter(email=username).first() 
            
        # 如果是11位數字且開頭為1,判定為手機登錄
        elif re.match(r'^1[0-9]{10}$', username):
            user = models.User.objects.filter(mobile=username).first()
            
        # 兩個都不匹配的話,就判定為用戶名登錄
        else:
            user = models.User.objects.filter(username=username).first()

        # 如果用戶不存在,就是信息有誤
        if not user:
            raise ValidationError({'username': '賬號有誤'})
		
        # 如果用戶存在,但是檢測密碼有問題,報錯密碼有誤
        if not user.check_password(password):
            raise ValidationError({'password': '密碼有誤'})

        return user
    
# views.py
from rest_framework.views import APIView
from . import models, serializers
from utils.response import APIResponse
class LoginAPIView(APIView):
    authentication_classes = []
    permission_classes = []
    
    # 以post的方式接受前台發送的數據
    def post(self, request, *args, **kwargs):
        # 將前台傳來的數據傳送到序列化對象中,完成校驗
        serializer = serializers.LoginSerializer(data=request.data)
        serializer.is_valid(raise_exception=True)
        return APIResponse(msg='login success', data={
            'username': serializer.user.username,
            'token': serializer.token
        })


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM