0909自我總結 drf框架中jwt 一.模塊的安裝 :http://getblimp.github.io/django rest framework jwt/ 他是個第三方的開源項目 :`pip install djangorestframework jwt` 使用 設定好的jwt 測試介面:po ...
0909自我總結
drf框架中jwt
一.模塊的安裝
官方
:http://getblimp.github.io/django-rest-framework-jwt/
他是個第三方的開源項目
安裝
:pip install djangorestframework-jwt
使用自帶
設定好的jwt
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請求
"""
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認證
第三方
與session認證區別他不用再去查sessionid
表,只要查user
表就可以了
rest_framework_jwt.authentication.JSONWebTokenAuthentication
ajax請求通過認證:
請求頭中要攜帶 authorization,值為 jwt空格token
基於jwt、其它
自定義
1)自定義認證類,繼承BaseAuthentication(或其子類),重寫authenticate
2)authenticate中完成
拿到認證標識 auth
反解析出用戶 user
前兩步操作失敗 返回None => 游客
前兩步操作成功 返回user,auth => 登錄用戶
註
:如果在某個分支拋出異常,直接定義失敗 => 非法用戶
四.自定義認證,基於jwt
其實就是在jwt的源碼基礎上進行相關的修改
最簡單的修改
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把這個函數加進去
'DEFAULT_AUTHENTICATION_CLASSES': [
'我們自定義認證函數的對象',
],
我們做局部設置就在我們自定義的類中添加
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把這個函數加進去
'DEFAULT_PERMISSION_CLASSES': [
'我們自定義許可權函數的路徑',
],
我們做局部設置就在我們自定義的類中添加
permission_classes = [我們自定義認證函數的對象]
六.自定義訪問次數設置
"""
系統:
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 => 不限制
有限制信息 返回限制信息字元串 => 有限制
"""
自定義頻率類:一分鐘一個手機號只允許訪問一次介面
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
'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即可
七.全局設置有效時間以及jwt的名稱
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開頭
八.關於jwt自定製獲取token
源碼在rest_framework_jwt.seriallizers.py中
JSONWebTokenSerializer類
payload = jwt_payload_handler(user)
return {
'token': jwt_encode_handler(payload),
'user': user
}
我們如果自定義有幾個關鍵點把握就好了一個是jwt_payload_handler
的方法
一個是 user對象
所有如果我們要在登入的時候拋出token
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
})