出自:https://blog.csdn.net/yueguangMaNong/article/details/90519819
前言
我為什么要寫這篇文章猿涨?
最近由于工作需要聂宾,在原來的項目(Django,B/S)的基礎上需要增加和客戶端(Client)的通信救崔。這個時候session機制就不合適了,需要用到token。我選擇了Rest Framework 插件來完成這個功能逆害。關于django token的教程晋南,網上一搜一大堆惠猿,我為什么還要寫?主要由以下幾點:
飲水思源:寫這篇文章之前,我被各種bug折磨了一天偶妖,借鑒了兩位大佬的東西姜凄,歡迎去他們下面查看原文。
http://www.reibang.com/p/e0a206212df4
http://www.reibang.com/p/078fb116236e?
網上文章雖然多趾访,但多數(shù)是大神的教程态秧,神龍見首不見尾,原理一講扼鞋,一段關鍵代碼貼上來就完事了申鱼,對我這樣的學渣實在是不怎么友好。
做個學習記錄云头,給后來者一個方便
開始
整個過程總共分為以下幾步
創(chuàng)建一個 Django項目捐友,新建一個app(廢話)
安裝?django-rest-framework (還是廢話)
在settings中注冊rest-framework,配置 REST_FRAMEWORK
編寫自己的驗證方法溃槐,并加入REST_FRAMEWORK配置
在登陸視圖中創(chuàng)建 Token
在操作視圖中添加 Toekn 驗證
使用postman 驗證
創(chuàng)建Django項目
我相信搜索到這篇文章的都不是0基礎匣砖,起碼創(chuàng)建項目,新增一個app是沒問題的吧昏滴。我就略過這步了猴鲫。如果有需要指導創(chuàng)建項目,新增app的谣殊,請留言拂共!
安裝 REST_FRAMEWORK
> pip install django-rest-framework
注冊 rest-framework并配置
不需要導入這個包,直接注冊即可
INSTALLED_APPS = [
? ? ...
? ? 'rest_framework.authtoken'
? ? ...
]
配置一下姻几, 這個是必須的匣缘,不然返回數(shù)據的時候回報錯
REST_FRAMEWORK = {
? ? 'DEFAULT_RENDERER_CLASSES': (
? ? ? ? 'rest_framework.renderers.JSONRenderer',
? ? ? ? 'rest_framework.renderers.BrowsableAPIRenderer',
? ? ),
}
編寫我們的驗證方法
在app下面創(chuàng)建一個名為auth.py的文件,將下面的內容寫入這個文件中
import datetime
from django.utils.translation import ugettext_lazy
from django.core.cache import cache
from rest_framework.authentication import BaseAuthentication
from rest_framework import exceptions
from rest_framework.authtoken.models import Token
from rest_framework import HTTP_HEADER_ENCODING
# 獲取請求頭信息
def get_authorization_header(request):
? ? auth = request.META.get('HTTP_AUTHORIZATION', b'')
? ? if isinstance(auth, type('')):
? ? ? ? auth = auth.encode(HTTP_HEADER_ENCODING)
? ? return auth
# 自定義認證方式鲜棠,這個是后面要添加到設置文件的
class ExpiringTokenAuthentication(BaseAuthentication):
? ? model = Token
? ? def authenticate(self, request):
? ? ? ? auth = get_authorization_header(request)
? ? ? ? if not auth:
? ? ? ? ? ? return None
? ? ? ? try:
? ? ? ? ? ? token = auth.decode()
? ? ? ? except UnicodeError:
? ? ? ? ? ? msg = ugettext_lazy("無效的Token肌厨, Token頭不應包含無效字符")
? ? ? ? ? ? raise exceptions.AuthenticationFailed(msg)
? ? ? ? return self.authenticate_credentials(token)
? ? def authenticate_credentials(self, key):
? ? ? ? # 嘗試從緩存獲取用戶信息(設置中配置了緩存的可以添加,不加也不影響正常功能)
? ? ? ? token_cache = 'token_' + key
? ? ? ? cache_user = cache.get(token_cache)
? ? ? ? if cache_user:
? ? ? ? ? ? return cache_user, cache_user? # 這里需要返回一個列表或元組豁陆,原因不詳
? ? ? ? # 緩存獲取到此為止
? ? ? ? # 下面開始獲取請求信息進行驗證
? ? ? ? try:
? ? ? ? ? ? token = self.model.objects.get(key=key)
? ? ? ? except self.model.DoesNotExist:
? ? ? ? ? ? raise exceptions.AuthenticationFailed("認證失敗")
? ? ? ? if not token.user.is_active:
? ? ? ? ? ? raise exceptions.AuthenticationFailed("用戶被禁用")
? ? ? ? # Token有效期時間判斷(注意時間時區(qū)問題)
? ? ? ? # 我在設置里面設置了時區(qū) USE_TZ = False柑爸,如果使用utc這里需要改變。
? ? ? ? if (datetime.datetime.now() - token.created) > datetime.timedelta(hours=0.1*1):
? ? ? ? ? ? raise exceptions.AuthenticationFailed('認證信息已過期')
? ? ? ? # 加入緩存增加查詢速度盒音,下面和上面是配套的表鳍,上面沒有從緩存中讀取,這里就不用保存到緩存中了
? ? ? ? if token:
? ? ? ? ? ? token_cache = 'token_' + key
? ? ? ? ? ? cache.set(token_cache, token.user, 600)
? ? ? ? # 返回用戶信息
? ? ? ? return token.user, token
? ? def authenticate_header(self, request):
? ? ? ? return 'Token'
將這個驗證方法注冊到配置里面
REST_FRAMEWORK = {
? ? # 新增的
? ? 'DEFAULT_AUTHENTICATION_CLASSES': (
? ? ? ? 'apps.xtauth.auth.ExpiringTokenAuthentication',? # 根據自己的實際情況填寫路徑
? ? ),
? ? # 下面是剛剛已經寫好的
? ? 'DEFAULT_RENDERER_CLASSES': (
? ? ? ? 'rest_framework.renderers.JSONRenderer',
? ? ? ? 'rest_framework.renderers.BrowsableAPIRenderer',
? ? ),
}
一切準備工作就緒祥诽,下面就開始使用它了譬圣。
創(chuàng)建Token
創(chuàng)建token首先要有用戶,我這里直接使用系統(tǒng)的User表雄坪,就不直接創(chuàng)建了
下面的代碼寫在視圖views.py
from django.http import JsonResponse
from django.shortcuts import HttpResponse
from rest_framework.decorators import api_view
from rest_framework.authtoken.models import Token
from django.contrib import auth
@api_view(['POST'])
def login(request):
? ? receive = request.data
? ? username = receive.get('username')
? ? password = receive.get('password')
? ? user = auth.authenticate(username=username, password=password)
? ? if not user:
? ? ? ? return HttpResponse("用戶名和密碼不匹配")
? ? # 校驗通過
? ? # 刪除原有的Token
? ? old_token = Token.objects.filter(user=user)
? ? old_token.delete()
? ? # 創(chuàng)建新的Token
? ? token = Token.objects.create(user=user)
? ? return JsonResponse({"username": user.username, "token": token.key})
說明一下:
裝飾器api.view是rest framework提供的厘熟,它可以指定請求方法,如果這里使用了它,那么針對這個方法的CSRF就會失效绳姨,所以不需要再額外添加 csrf_exempt 裝飾器
這個裝飾器會給request對象添加一個data屬性登澜,post上傳的數(shù)據都可以在這里面直接獲取,示例中用戶名和密碼就是直接從里面取出來的飘庄,上傳的json數(shù)據已經轉換好了脑蠕,直接獲取即可。
auth.authenticate() 方法是Django提供的驗證用戶名和密碼是否匹配的跪削,和rest framework毛關系都沒有
創(chuàng)建token是通過Token.objects.create() 創(chuàng)建的谴仙。創(chuàng)建之前最好先刪除原來的token,否則一個用戶多次創(chuàng)建token會報錯的碾盐,數(shù)據庫唯一約束
驗證Token
創(chuàng)建好了之后晃跺,自然就該驗證了,寫一個什么函數(shù)驗證一下
@api_view(['POST'])
def do_something(request):
? ? receive = request.data
? ? print(receive)
? ? if request.user.is_authenticated:? # 驗證Token是否正確
? ? ? ? print("Do something...")
? ? ? ? return JsonResponse({"msg": "驗證通過"})
? ? else:
? ? ? ? print("驗證失敗")
? ? ? ? return JsonResponse({"msg": "驗證失敗"})
說明:
request.user.is_authenticated:這個是驗證Token是否正確的廓旬。user對象也是使用了api_view裝飾器之后添加的哼审。
很明顯谐腰,rest framework孕豹, 我們應該遵循RestFul規(guī)范,返回json格式數(shù)據才是中規(guī)中矩的十气。如果起前面的設置中REST_FRAMEWORK下面沒有配置 rest_framework.renderers.JSONRenderer 的話励背,這里會報錯排监,一定要添加
路由配置
from django.urls import path
from . import views
urlpatterns = [
? ? path('login/', views.login),
? ? path('doSomething/', views.do_something),
]
很簡單支救,就是簡單的登陸和操作驗證兩個url
Postman測試
功能都已經完成了骨宠,就是這么簡單楼眷,最后我們來測試一下
先創(chuàng)建一個用戶:
用戶名:zhangsan? ? ? ?密碼: 123456
然后使用Postman登陸獲取Token
可以看到踱卵,一切都那么自然疏旨,成功獲取到了Token
接下來就來測試一下這個token的正確性
過期的情況
由于之前時間設置得太短贝咙,導致寫完文字后token已經失效了虑啤,尷尬鸳慈。來個正確的打開方式
成功的示范
注意幾點:
headers里面的key只能是【Authorization】饱溢,別的名稱將會驗證失敗,至于能不能走芋,應該是可以的绩郎,只是我不知道(手動狗頭)
token一定要在有效期內才能驗證通過(廢話),時間在auth.py文件里面設置翁逞,注意時區(qū)的問題肋杖。自己試驗,建議設置USE_TZ = False挖函,簡單粗暴状植。
OK,大功告成,希望這篇文章能夠幫你浅萧,歡迎留言回復逐沙。