1. 程式人生 > 其它 >Go 語言實現的中國行政區劃程式碼包 gbt2260

Go 語言實現的中國行政區劃程式碼包 gbt2260

一、JWT認證

JWT構成

JWT分為三段式:頭、體、簽名(head、payload、sgin)

頭和體是可逆加密的,讓伺服器可以反解析出user物件,簽名是不可逆加密,保證整個token的安全性的。

頭、體、簽名三部分,都是採用JSON格式的字串,進行加密,可逆加密一般蠶蛹base64演算法,不可逆加密一般採用hash(md5)演算法

  • 頭中的內容是基本資訊:專案資訊等、
{
    'company': '專案資訊',
    ...
}
  • 體中的內容是關鍵資訊:使用者主鍵、使用者名稱、簽發時客戶端資訊(裝置號,地址)、過期時間
{
    'user_id': 2,
    'username': 'xiaoyang',
    ...
}
  • 簽名中的內容是安全資訊:頭的加密結果 + 體的加密結果 + 伺服器不對外公開的安全碼 進行md5加密
{
	"head": "頭的加密字串",
	"payload": "體的加密字串",
	"secret_key": "安全碼"
}

過期時間配置:

import datetime
JWT_AUTH={
    'JWT_EXPIRATION_DELTA': datetime.timedelta(days=7), 	# 過期時間,手動配置
}

校驗過程

  1. 將token按 ‘ . ’ 拆分為三段字串
  2. 第一段:頭加密字串,一般不需要做任何處理
  3. 第二段:體加密字串,要反解出使用者主鍵,通過主鍵從User表中就能得到登入使用者,過期時間和裝置資訊都是安全資訊,確保token沒有過期,且是同一裝置來的
  4. 在用 第一段 + 第二段 + 伺服器安全碼 進行md5加密,與第三段 ”簽名字串“ 進行對比校驗,通過後才能代表第二段校驗得到的user物件是合法的登入使用者

DRF專案的JWT認證開發流程

  1. 用賬號密碼訪問登入介面,登入介面邏輯中呼叫簽發token演算法,得到token,返回給客戶端,客戶端自己存到Cookies中
  2. 校驗token的演算法應該寫在認證類中(在認證類中呼叫),全域性配置給認證元件,所有檢視類請求,都會進行認證校驗,所以請求帶了token,就會反解析出user物件,在檢視類中用request.user就能訪問登入的使用者。

第三方JWT認證

使用 JWT的第三方認證模組 django-rest-framework-jwk

安裝:pip install djangorestframework-jwk

簡單使用:

1、使用者密碼認證成功獲取token

# 首先建立超級使用者 》》python manage.py createsuperuser

from rest_framework_jwt.views import ObtainJSONWebToken, VerifyJSONWebToken, RefreshJSONWebToken

# 基類:JSONWebTokenAPIView 繼承了APIView
# ObtainJSONWebToken,VerifyJSONWebToken,RefreshJSONWebToken都繼承了JSONWebTokenAPIView

urlpatterns = [
    path('login/', ObtainJSONWebToken.as_view()),
]

# 使用post請求帶著使用者和密碼去訪問http://127.0.0.1:8000/login/,就會得到token

2、檢視訪問 JWT認證類

但是 第三方JWT的認證必須要在請求頭中新增 Authorization 和對應的 JWT +token引數,才會進行認證,否則就不認證(不好用需要從新寫)

from rest_framework.views import APIView
from rest_framework.response import Response
from rest_framework_jwt.authentication import JSONWebTokenAuthentication


class TestView(APIView):
    
    # 區域性配置
    authentication_classes = [JSONWebTokenAuthentication]

    def get(self, request):
        return Response('認證測試')
    
    
# 全域性配置需要在settings.py配置
REST_FRAMEWORK = {
    'DEFAULT_AUTHENTICATION_CLASSES': [
        'rest_framework_jwt.authentication.JSONWebTokenAuthentication',
    ]
}

自定義JWT認證類

由於第三方的 JWT認證必須要在請求中新增 Authorization欄位才會認證,否則不認證直接通過,所以不使用它,自己寫一個基於第三方JWT的認證類,這樣請求頭中沒有Authorization欄位,就會認證失敗。

# 由於使用的是基於第三方的認證,所有還是要繼承它,並且使用一些它的方法,而且還要重寫authenticate方法
# app_auth.py
from rest_framework_jwt.authentication import BaseJSONWebTokenAuthentication
from rest_framework_jwt.utils import jwt_decode_handler
from rest_framework import exceptions


class TestAuth(BaseJSONWebTokenAuthentication):

    def authenticate(self, request):
        # 獲取JWT的token值
        jwt_value = request.META.get('HTTP_AUTHORIZATION')

        try:
            # 認證
            payload = jwt_decode_handler(jwt_value)
        except Exception:
            raise exceptions.AuthenticationFailed('認證失敗')

        # 獲取使用者物件
        user = self.authenticate_credentials(payload)
        return user, None
    
    
    
# views.py
# 區域性使用
from rest_framework.views import APIView
from rest_framework.response import Response
from app01.app_auth import TestAuth


class TestView(APIView):
    # 區域性配置
    authentication_classes = [TestAuth]

    def get(self, request):
        return Response('認證測試')
    
    
# 全域性配置需要在settings.py配置
REST_FRAMEWORK = {
    'DEFAULT_AUTHENTICATION_CLASSES': [
        'app01.app_auth.TestAuth',
    ]
}

二、手動簽發token(多方式登入)

當用戶使用使用者名稱,手機號,郵箱等都可以登入

如:前端需要傳的資料格式

{
    "username":"xiaoyang/13313311333/[email protected]",
	"password":"111111ys"
}

url

# post請求呼叫LoginView檢視中的login函式
re_path('login/', views.LoginView.as_view({'post': 'login'}))

views.py

from rest_framework.viewsets import ViewSet
from rest_framework.response import Response
from app01.ser import UserModelSerializer

class LoginView(ViewSet):

    def login(self, request):
        
        # 序列化一個類
        login_ser = UserModelSerializer(data=request.data, context={})
        
        # 驗證,會呼叫序列化器中的鉤子函式
        if login_ser.is_valid():
            token = login_ser.context.get('token')
            username = login_ser.context.get('username')
            
            # 驗證成功返回使用者名稱和token
            return Response({'username': username, 'token': token})
        else:
            # 驗證失敗返回錯誤資訊
            return Response(login_ser.errors)

ser.py

from rest_framework import serializers
from rest_framework.exceptions import ValidationError
from rest_framework_jwt.utils import jwt_encode_handler, jwt_payload_handler
from app01.models import User
import re

class UserModelSerializer(serializers.ModelSerializer):
    # 重新覆蓋username欄位,資料庫中它是唯一的(unique),post會認為是你自己儲存資料,自己校驗沒過
    username = serializers.CharField(max_length=16)

    class Meta:
        model = User
        fields = ['username', 'password']

    # 全域性鉤子
    def validate(self, attrs):
        username = attrs.get('username')
        password = attrs.get('password')

        if re.match(r'^1[3-9]\d{9}', username):
            # 電話登入
            user = User.objects.filter(phone=username).first()
        elif re.match(r'.*@.*', username):
            # 郵箱登入
            user = User.objects.filter(email=username).first()
        else:
            # 使用者名稱登入
            user = User.objects.filter(username=username).first()

        if user:
            # 校驗密碼,因為用的是auth模組,所以使用check_password
            user.check_password(password)

            payload = jwt_payload_handler(user)  # 放入使用者生成payload
            token = jwt_encode_handler(payload)  # 放入payload生成token

            self.context['token'] = token
            self.context['username'] = user.username

            return attrs
        else:
            raise ValidationError('使用者名稱或密碼錯誤')
學習之旅