官方
:http://getblimp.github.io/django-rest-framework-jwt/python
他是個第三方的開源項目git
安裝
:pip install djangorestframework-jwt
github
使用自帶
設定好的jwtweb
from django.urls import path from rest_framework_jwt.views import obtain_jwt_token urlpatterns = [ path('login/', obtain_jwt_token), ] ''' path('login/', obtain_jwt_token)其實至關於path('login/', ObtainJSONWebToken.as_view()) 由於咱們之間進源碼能夠看到 obtain_jwt_token = ObtainJSONWebToken.as_view() #得到 refresh_jwt_token = RefreshJSONWebToken.as_view() #刷新 verify_jwt_token = VerifyJSONWebToken.as_view() #驗證 '''
測試接口:post請求ajax
""" postman發生post請求 接口:http://api.luffy.cn:8000/user/login/ 數據: { "username":"admin", "password":"admin" } """
""" jwt:json web tokens 採用json格式在web上傳輸的 認證字符串 jwt字符串:頭.載荷.簽名 頭:公司基本信息、項目組基本信息、常規加密算法名 載荷:用戶信息、過時時間 簽名:頭、載荷、祕鑰 {頭信息字典,採用base64加密算法}.{載荷信息字典,採用base64加密算法}.{頭加密串、載荷加密串、服務器祕鑰,採用hs256加密算法} base64是可逆加密 hash256是不可逆加密 咱們通常只會將帳號信息,過時時間放載荷裏面,通常把密碼什麼重要信息丟簽名裏面 """
""" 系統:session認證 rest_framework.authentication.SessionAuthentication ajax請求經過認證: cookie中要攜帶 sessionid、csrftoken,請求頭中要攜帶 x-csrftoken 第三方:jwt認證 rest_framework_jwt.authentication.JSONWebTokenAuthentication ajax請求經過認證: 請求頭中要攜帶 authorization,值爲 jwt空格token 自定義:基於jwt、其它 1)自定義認證類,繼承BaseAuthentication(或其子類),重寫authenticate 2)authenticate中完成 拿到認證標識 auth 反解析出用戶 user 前兩步操做失敗 返回None => 遊客 前兩步操做成功 返回user,auth => 登陸用戶 注:若是在某個分支拋出異常,直接定義失敗 => 非法用戶 """
""" 系統: 1)AllowAny:容許全部用戶,校驗方法直接返回True 2)IsAuthenticated:只容許登陸用戶 必須request.user和request.user.is_authenticated都經過 3)IsAuthenticatedOrReadOnly:遊客只讀,登陸用戶無限制 get、option、head 請求無限制 前臺請求必須校驗 request.user和request.user.is_authenticated 4)IsAdminUser:是不是後臺用戶 校驗 request.user和request.user.is_staff is_staff(能夠登陸後臺管理系統的用戶) 自定義:基於auth的Group與Permission表 1)自定義權限類,繼承BasePermission,重寫has_permission 2)has_permission中完成 拿到登陸用戶 user <= request.user 校驗user的分組或是權限 前兩步操做失敗 返回False => 無權限 前兩步操做成功 返回True => 有權限 """
""" 系統: 1)AnonRateThrottle:對同一IP遊客的限制 2)UserRateThrottle:對同一IP登陸用戶的限制 必須在settings.py中 'DEFAULT_THROTTLE_RATES': { 'user': '10/min', # 登陸的用戶一分鐘能夠訪問10次 'anon': '3/min', # 遊客一分鐘能夠訪問3次 } 在視圖類中: class TempAPIView(APIView): ... throttle_classes = [AnonRateThrottle, UserRateThrottle] 自定義:基於auth的Group與Permission表 1)自定義頻率類,繼承SimpleRateThrottle,重寫get_cache_key,明確scope SimpleRateThrottle已經幫咱們實現了 allow_request、wait 2)scope與settings.py的DEFAULT_THROTTLE_RATES配合使用 3)get_cache_key中完成 拿到限制信息 ident <= request中獲取 沒有限制信息 返回None => 不限制 有限制信息 返回限制信息字符串 => 有限制 """
其實就是在jwt的源碼基礎上進行相關的修改
算法
最簡單的修改django
from rest_framework.exceptions import AuthenticationFailed import jwt from rest_framework_jwt.authentication import BaseJSONWebTokenAuthentication from rest_framework_jwt.authentication import jwt_decode_handler from rest_framework.authentication import BaseAuthentication def authenticate(self, request): auth = 從request中獲得 user = 從auth中獲得 if not user: return None return user, auth
若是咱們自定製了一個權限咱們進行全局設置必須本身在setting把這個函數加進去
json
REST_FRAMEWORK = { 'DEFAULT_AUTHENTICATION_CLASSES': [ # django默認session校驗:校驗規則 遊客 及 登陸用戶 # 'rest_framework.authentication.SessionAuthentication', # 'rest_framework.authentication.BasicAuthentication', # 'rest_framework_jwt.authentication.JSONWebTokenAuthentication', ], }
咱們作局部設置就在咱們自定義的類中添加
api
authentication_classes = [咱們自定義認證函數的對象]
也是改源碼
服務器
""" 系統: 1)AllowAny:容許全部用戶,校驗方法直接返回True 2)IsAuthenticated:只容許登陸用戶 必須request.user和request.user.is_authenticated都經過 3)IsAuthenticatedOrReadOnly:遊客只讀,登陸用戶無限制 get、option、head 請求無限制 前臺請求必須校驗 request.user和request.user.is_authenticated 4)IsAdminUser:是不是後臺用戶 校驗 request.user和request.user.is_staff is_staff(能夠登陸後臺管理系統的用戶) 自定義:基於auth的Group與Permission表 1)自定義權限類,繼承BasePermission,重寫has_permission 2)has_permission中完成 拿到登陸用戶 user <= request.user 校驗user的分組或是權限 前兩步操做失敗 返回False => 無權限 前兩步操做成功 返回True => 有權限 """
#根據用戶分組信息設置相關權限 from rest_framework.permissions import BasePermission class AdminPermission(BasePermission): # 繼承BasePermission,重寫has_permission def has_permission(self, request, view): # 有權限,返回True # 無權限,返回False user = request.user if not user: return False # 用戶是 管理員 分組 (管理員分組是Group表中的一條自定義記錄) if not user.groups.filter(name='管理員'): return False # 登陸的用戶必須是自定義管理員分組成員 return True
若是咱們自定製了一個權限全局設置咱們必須本身在setting把這個函數加進去
自定義類的路徑api.authentications.JWTAuthentication
#自定義一個權限 from rest_framework.authentication import BaseAuthentication from rest_framework.exceptions import AuthenticationFailed import jwt from rest_framework_jwt.authentication import BaseJSONWebTokenAuthentication from rest_framework_jwt.authentication import jwt_decode_handler class JWTAuthentication(BaseJSONWebTokenAuthentication): # 自定義認證類,重寫authenticate方法 def authenticate(self, request): # 認證經過,返回user,auth # 認證失敗,返回None auth = request.META.get('HTTP_AUTH') # 前臺用auth攜帶token if not auth: return None try: payload = jwt_decode_handler(auth) # 出現jwt解析異常,直接拋出異常,表明非法用戶,也能夠返回None,做爲遊客處理 except jwt.ExpiredSignature: raise AuthenticationFailed('token已過時') except: raise AuthenticationFailed('token非法') user = self.authenticate_credentials(payload) return (user, auth)
REST_FRAMEWORK = { 'DEFAULT_AUTHENTICATION_CLASSES': [ # '咱們自定義權限函數的路徑', 'api.authentications.JWTAuthentication', ], 'DEFAULT_PERMISSION_CLASSES': [ # 'rest_framework.permissions.AllowAny', # 全局配置:一站式網站(全部操做都須要登陸後才能訪問) # 'rest_framework.permissions.IsAuthenticated', ], }
咱們作局部設置就在咱們自定義的類中添加
permission_classes = [咱們自定義認證函數的對象]
""" 系統: 1)AnonRateThrottle:對同一IP遊客的限制 2)UserRateThrottle:對同一IP登陸用戶的限制 必須在settings.py中 REST_FRAMEWORK = { 'DEFAULT_THROTTLE_RATES': { 'user': '10/min', # 登陸的用戶一分鐘能夠訪問10次 'anon': '3/min', # 遊客一分鐘能夠訪問3次 } } 在視圖類中: class TempAPIView(APIView): ... throttle_classes = [AnonRateThrottle, UserRateThrottle] 自定義:基於auth的Group與Permission表 1)自定義頻率類,繼承SimpleRateThrottle,重寫get_cache_key,明確scope SimpleRateThrottle已經幫咱們實現了 allow_request、wait 2)scope與settings.py的DEFAULT_THROTTLE_RATES配合使用 3)get_cache_key中完成 拿到限制信息 ident <= request中獲取 沒有限制信息 返回None => 不限制 有限制信息 返回限制信息字符串 => 有限制 """
自定義頻率類:一分鐘一個手機號只容許訪問一次接口
from rest_framework.throttling import SimpleRateThrottle class ThreeMinRateThrottle(SimpleRateThrottle): scope = 'sms' def get_cache_key(self, request, view): # 對手機號頻率限制 ident = request.data.get('mobile') if not ident: # 爲發現限制條件,返回None表明不進行頻率限制 return None return self.cache_format % { 'scope': self.scope, 'ident': ident } # settings.py REST_FRAMEWORK = { 'DEFAULT_THROTTLE_RATES': { 'user': '10/min', # 登陸的用戶一分鐘能夠訪問10次 若是是 'anon': '3/min', # 遊客一分鐘能夠訪問3次 'sms': '1/min', #是咱們自定義的,默認只提供user以及anon } }
在視圖層 class UserListAPIView(ListAPIView): throttle_classes = [咱們自定義的方法路徑]
源碼裏關於時間的一段代碼
def parse_rate(self, rate): """ Given the request rate string, return a two tuple of: <allowed number of requests>, <period of time in seconds> """ if rate is None: return (None, None) num, period = rate.split('/') num_requests = int(num) duration = {'s': 1, 'm': 60, 'h': 3600, 'd': 86400}[period[0]] return (num_requests, duration)
這裏咱們能夠看出來是先/進行字符串切分而後取第一個字母
全部咱們這邊不必定用min表明分,只要開頭是m便可
import datetime JWT_AUTH = { 'JWT_EXPIRATION_DELTA': datetime.timedelta(seconds=30000),#d到期時間 'JWT_AUTH_HEADER_PREFIX': 'TOKEN', #咱們傳參數的時候開頭自定義內容,注意點這裏必須與下面的token中以宮格隔開 }
源碼中爲
USER_SETTINGS = getattr(settings, 'JWT_AUTH', None) #他是經過JWT_AUTH這個名字 ...... 'JWT_EXPIRATION_DELTA': datetime.timedelta(seconds=300), 'JWT_AUTH_HEADER_PREFIX': 'JWT', 系統默認以jwt開頭
源碼在rest_framework_jwt.seriallizers.py中
JSONWebTokenSerializer類
payload = jwt_payload_handler(user) return { 'token': jwt_encode_handler(payload), 'user': user }
咱們若是自定義有幾個關鍵點把握就行了一個是jwt_payload_handler
的方法
一個是 user對象
全部若是咱們要在登入的時候拋出token
import re from utils.response import APIResponse from rest_framework_jwt.serializers import jwt_encode_handler, jwt_payload_handler class LoginJWTAPIView(APIView): authentication_classes = () permission_classes = () def post(self, request, *args, **kwargs): # username可能攜帶的不止是用戶名,可能仍是用戶的其它惟一標識 手機號 郵箱 username = request.data.get('username') password = request.data.get('password') # 若是username匹配上手機號正則 => 多是手機登陸 if re.match(r'1[3-9][0-9]{9}', username): try: # 手動經過 user 簽發 jwt-token user = models.User.objects.get(mobile=username) except: return APIResponse(1, '該手機未註冊') # 郵箱登陸 等 # 帳號登陸 else: try: # 手動經過 user 簽發 jwt-token user = models.User.objects.get(username=username) except: return APIResponse(1, '該帳號未註冊') # 得到用戶後,校驗密碼並簽發token if not user.check_password(password): return APIResponse(1, '密碼錯誤') payload = jwt_payload_handler(user) token = jwt_encode_handler(payload) return APIResponse(0, 'ok', results={ 'username': user.username, 'mobile': user.mobile, 'token': token })
# settings.py # jwt配置 import datetime JWT_AUTH = { 'JWT_EXPIRATION_DELTA': datetime.timedelta(seconds=30000), 'JWT_AUTH_HEADER_PREFIX': 'TOKEN', }