JWT:
1、組成: header.payload.signature 頭.載荷.簽名 2、示例: eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ1c2VyX2lkIjoxLCJ1c2VybmFtZSI6Im93ZW4iLCJleHAiOjE1NTgzMDM1NDR9.4j5QypLwufjpqoScwUB9LYiuhYcTw1y4dPrvnv7DUyo 3:介紹: header:通常存放如何處理token的方式:加密的算法、是否有簽名等 payload:數據的主體部分:用戶信息、發行者、過時時間等 signature:簽名:將header、payload再結合密碼鹽總體處理一下
工做原理: 1) jwt = base64(頭部).base64(載荷).hash256(base64(頭部).base(載荷).密鑰) 2) base64是可逆的算法、hash256是不可逆的算法 3) 密鑰是固定的字符串,保存在服務器
官網:https://github.com/jpadilla/django-rest-framework-jwt
安裝:pip install djangorestframework-jwt
使用:通常是搭配xadmin去實現後臺管理python
得到token:git
在user/url.py裏: from django.urls import path from rest_framework_jwt.views import obtain_jwt_token urlpatterns = [ path('login/', obtain_jwt_token), ] 利用postman測試發射post請求: 接口:http://127.0.0.1:8000/user/login/ 數據: { "username":"admin", "password":"admin" }
可能存在一些問題: Unable to log in with provided credentials 緣由是這種算法內部是須要使用auth_user表去獲取用戶的帳號和密碼數據的,而咱們沒有建立相關的數據,所以須要建立一些數據: python manage.py createsuperuser
再次嘗試:獲得token值:
"token": "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ1c2VyX2lkIjoxLCJ1c2VybmFtZSI6ImFkbWluIiwiZXh
wIjoxNTY5MjAyODc1LCJlbWFpbCI6IjEzMDYzNDczMzgwQDE2My5jb20ifQ.JgbSItmx0sa4JHuA5I4J58gcoeCgTLKZaPj0T-f5OwI"
雖然說拿到了後端生成的token值,可怎麼讓其真正的實現認證功能呢:github
在settinngs.py裏配置jwt的相關數據 import datetime JWT_AUTH = { # 過時時間 'JWT_EXPIRATION_DELTA': datetime.timedelta(days=1), # 自定義認證類 'JWT_RESPONSE_PAYLOAD_HANDLER': 'user.utils.jwt_response_payload_handler', }
序列化組件 user/serializers.py from rest_framework import serializers from .models import User class UserModelSerializer(serializers.ModelSerializer): """輪播圖序列化器""" class Meta: model = User fields = ["username", "mobile"] 須要注意的是:User這張表繼承的是 from django.contrib.auth.models import AbstractUser ,由於jwt內部走的是auth_user這張表。 同時還須要在配置裏將 AUTH_USER_MODEL = 'app的名字.User' 添加上,表示重寫了auth的認證表 同時:由於重寫了auth表,須要刪除一些遷移記錄文件:django/contrib/admin/migrations django/contrib/auth/migrations xadmin/migrations 若是使用xadmin做爲後臺管理使用 reversion/migrations 同上 user/migrations
在user/models.py下: from django.db import models # Create your models here. from django.contrib.auth.models import AbstractUser class User(AbstractUser): mobile = models.CharField(max_length=32,default=0) age = models.IntegerField(default=0)
自定義response的返回結果: user/utils.py from .serializers import UserModelSerializers def jwt_response_payload_handler(token, user=None, request=None): return { 'token': token, # 拿到的是一個序列化後的對象,多個對象加上many=True 'user': UserModelSerializer(user).data } # restful 規範 # return { # 'status': 0, # 'msg': 'OK', # 'data': { # 'token': token, # 'username': user.username # } # }
上述操做完成了jwt的自定義返回值,如今須要實現的是jwt全局認證,基於drf的認證組件實現算法
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): # 下面的 token獲取,能夠直接 request.META.get()去獲取 token = get_authorization_header(request) # 能夠在此處 對請求頭的字段作出一些限制,實現反扒 if not token: raise AuthenticationFailed('Authorization字段是必須的') try: payload = jwt_decode_handler(token) except jwt.ExpiredSignature: raise AuthenticationFailed('簽名過時') except jwt.DecodeError: raise AuthenticationFailed('錯誤的解碼簽名') except jwt.InvalidTokenError: raise AuthenticationFailed('非法用戶') user = self.authenticate_credentials(payload) return (user, token)
固然了,須要在settings.py裏完成相關配置:全局啓用django
EST_FRAMEWORK = { # 認證模塊 'DEFAULT_AUTHENTICATION_CLASSES': ( 'user.authentications.JSONWebTokenAuthentication', ), } 須要知道,drf_jwt是基於drf框架實現的
局部啓用禁用:任何一個cbv類首行 # 局部禁用 authentication_classes = [] # 局部啓用 from user.authentications import JSONWebTokenAuthentication authentication_classes = [JSONWebTokenAuthentication]
上述操做時針對於用戶密碼而言的,若是是多方式登錄如何利用jwt實現呢?後端
from django.contrib.auth.backends import ModelBackend from .models import User import re class JWTModelBackend(ModelBackend): def authenticate(self, request, username=None, password=None, **kwargs): """ :param request: :param username: 前臺傳入的用戶名,能夠是多方式下的數據 :param password: 前臺傳入的密碼 :param kwargs: :return: """ try: if re.match(r'^1[3-9]\d{9}$', username): user = User.objects.get(mobile=username) elif re.match(r'.*@.*', username): user = User.objects.get(email=username) else: user = User.objects.get(username=username) except User.DoesNotExist: return None # 認證失敗就返回None便可,jwt就沒法刪除token # 用戶存在,密碼校驗經過,是活着的用戶 is_active字段爲1 if user and user.check_password(password) and self.user_can_authenticate(user): return user # 認證經過返回用戶,交給jwt生成token
配置多方式登錄: settings.py AUTHENTICATION_BACKENDS = ['user.utils.JWTModelBackend']
手動簽發jwt: 擁有原生登錄基於model類user對象簽發 jwtapi
from rest_framework_jwt.settings import api_settings服務器
jwt_payload_handler = api_settings.JWT_PAYLOAD_HANDLER
jwt_encode_handler = api_settings.JWT_ENCODE_HANDLERrestful
payload = jwt_payload_handler(user)
token = jwt_encode_handler(payload)app
# 瞭解,原生視圖# 原生APIView能夠實現手動簽發 jwtclass LoginAPIView(APIView): def post(self): # 完成手動簽發 pass