djangorestframework-jwt 食用方法css
json web token,通常用於用戶認證就是作用戶登陸的(先後端分離/微信小程序/app開發)
基於傳統的token認證html
用戶登陸,服務端返回token,並將token保存在服務端, 之後用戶再來訪問時,須要攜帶token,服務端獲取token後,再去數據庫中獲取token進行校驗
jwtpython
用戶登陸,服務端給用戶返回一個token(服務端不保存) 之後用戶再來訪問,須要攜帶token,服務端獲取token後,再作token的校驗----進行算法校驗 優點:相較於傳統的token相比,它無需在服務端保存token
第一步,用戶提交用戶名和密碼給服務器,若是登陸成功,使用jwt建立一個token,並給用戶返回.web
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c
注意:jwt生成的token是由三段字符串組成,而且用.鏈接起來的算法
第一段字符串,HEADER,內部包含了算法/token類型,數據庫
json轉換成字符串,而後作一個base64url加密(base64加密;+_),加密再加替換django
{ "alg": "HS256", "typ": "JWT" }
第二段字符串,PAYLOAD,自定義的值json
讓json轉換成字符串,而後作一個base64url加密(base64加密;+_),加密再加替換小程序
{ "sub": "1234567890", "name": "John Doe", "iat": 1516239022 # 前兩個隨便寫,最後一個是超時時間 }
第三段字符串:他會將第一段加密以後的值,和第二段加密以後的值經過.拼接起來後端
第一步:第1,2部分的密文拼接起來 eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ 第二步:對前兩部分密文進行HS256加密 + 加鹽 第三步:對HS256加密後的密文再作base64url加密
之後用戶再來訪問的時候,須要攜帶token,後端須要對token進行校驗
獲取token
第一步: 對token進行切割,經過點切割成三部分
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c
第二步: 對第二段進行 base64url 解密,並獲取payload信息,檢測token是否超時了?
{ "sub": "1234567890", "name": "John Doe", "iat": 1516239022 # 前兩個隨便寫,最後一個是超時時間 }
第三步 :把第1,2段拼接,再次執行HS256加密
第一步:第1,2部分的密文拼接起來 eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ 第二步:對前兩部分密文進行HS256加密 + 加鹽 密文 = base64解密(SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c) 若是密文相等,表示token未被修改過(認證經過)
pip install pyjwt
pyjwt.encode 生成token pyjwt.decode token解密
原理性的東西
#!/usr/bin/env python # -*- coding:utf-8 -*- from django.conf.urls import url, include from api import views urlpatterns = [ url('^login/$', views.LoginView.as_view()), url('^order/$', views.OrderView.as_view()), ]
from rest_framework.views import APIView from rest_framework.response import Response from utils.jwt_auth import create_token from extensions.auth import JwtQueryParamAuthentication, JwtAuthorizationAuthentication class LoginView(APIView): def post(self, request, *args, **kwargs): """ 用戶登陸 """ user = request.POST.get('username') pwd = request.POST.get('password') # 檢測用戶和密碼是否正確,此處能夠在數據進行校驗。 if user == 'wupeiqi' and pwd == '123': # 用戶名和密碼正確,給用戶生成token並返回 token = create_token({'username': 'wupeiqi'}) return Response({'status': True, 'token': token}) return Response({'status': False, 'error': '用戶名或密碼錯誤'}) class OrderView(APIView): # 經過url傳遞token authentication_classes = [JwtQueryParamAuthentication, ] # 經過Authorization請求頭傳遞token # authentication_classes = [JwtAuthorizationAuthentication, ] def get(self, request, *args, **kwargs): print(request.user, request.auth) return Response({'data': '訂單列表'}) def post(self, request, *args, **kwargs): print(request.user, request.auth) return Response({'data': '添加訂單'}) def put(self, request, *args, **kwargs): print(request.user, request.auth) return Response({'data': '修改訂單'}) def delete(self, request, *args, **kwargs): print(request.user, request.auth) return Response({'data': '刪除訂單'})
SECRET_KEY = '-(e4!74gqo8q@v-y#0cz9e7aeux4qx-pl1xw#05co4avr8r+r0' REST_FRAMEWORK = { "DEFAULT_VERSIONING_CLASS": 'rest_framework.versioning.URLPathVersioning', "ALLOWED_VERSIONS": ['v1', "v2"] #兩個認證版本 }
#!/usr/bin/env python # -*- coding:utf-8 -*- from rest_framework.authentication import BaseAuthentication from rest_framework import exceptions from utils.jwt_auth import parse_payload class JwtQueryParamAuthentication(BaseAuthentication): """ 用戶須要在url中經過參數進行傳輸token,例如: http://www.pythonav.com?token=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJleHAiOjE1NzM1NTU1NzksInVzZXJuYW1lIjoid3VwZWlxaSIsInVzZXJfaWQiOjF9.xj-7qSts6Yg5Ui55-aUOHJS4KSaeLq5weXMui2IIEJU """ def authenticate(self, request): token = request.query_params.get('token') payload = parse_payload(token) if not payload['status']: raise exceptions.AuthenticationFailed(payload) # 若是想要request.user等於用戶對象,此處能夠根據payload去數據庫中獲取用戶對象。 return (payload, token) class JwtAuthorizationAuthentication(BaseAuthentication): """ 用戶須要經過請求頭的方式來進行傳輸token,例如: Authorization:jwt eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJleHAiOjE1NzM1NTU1NzksInVzZXJuYW1lIjoid3VwZWlxaSIsInVzZXJfaWQiOjF9.xj-7qSts6Yg5Ui55-aUOHJS4KSaeLq5weXMui2IIEJU """ def authenticate(self, request): # 非登陸頁面須要校驗token authorization = request.META.get('HTTP_AUTHORIZATION', '') auth = authorization.split() if not auth: raise exceptions.AuthenticationFailed({'error': '未獲取到Authorization請求頭', 'status': False}) if auth[0].lower() != 'jwt': raise exceptions.AuthenticationFailed({'error': 'Authorization請求頭中認證方式錯誤', 'status': False}) if len(auth) == 1: raise exceptions.AuthenticationFailed({'error': "非法Authorization請求頭", 'status': False}) elif len(auth) > 2: raise exceptions.AuthenticationFailed({'error': "非法Authorization請求頭", 'status': False}) token = auth[1] result = parse_payload(token) if not result['status']: raise exceptions.AuthenticationFailed(result) # 若是想要request.user等於用戶對象,此處能夠根據payload去數據庫中獲取用戶對象。 return (result, token)
#!/usr/bin/env python # -*- coding:utf-8 -*- import jwt import datetime from jwt import exceptions JWT_SALT = 'iv%x6xo7l7_u9bf_u!9#g#m*)*=ej@bek5)(@u3kh*72+unjv=' def create_token(payload, timeout=20): """ :param payload: 例如:{'user_id':1,'username':'wupeiqi'}用戶信息 :param timeout: token的過時時間,默認20分鐘 :return: """ headers = { 'typ': 'jwt', 'alg': 'HS256' } payload['exp'] = datetime.datetime.utcnow() + datetime.timedelta(minutes=timeout) result = jwt.encode(payload=payload, key=JWT_SALT, algorithm="HS256", headers=headers).decode('utf-8') return result def parse_payload(token): """ 對token進行和發行校驗並獲取payload :param token: :return: """ result = {'status': False, 'data': None, 'error': None} try: verified_payload = jwt.decode(token, JWT_SALT, True) result['status'] = True result['data'] = verified_payload except exceptions.ExpiredSignatureError: result['error'] = 'token已失效' except jwt.DecodeError: result['error'] = 'token認證失敗' except jwt.InvalidTokenError: result['error'] = '非法的token' return result
pip3 install djangorestframework-jwt 和上面的相似不建議使用,就是加了一些配置文件,只能在drf中用,太侷限了
djangorestframework-jwt 本質是調用pyjwt實現的
使用場景兩端開發,app,小程序開發!