django項目前准備:三、方式一:優化Django Rest Framework 的Token驗證功能,增加過期時間


優化Django Rest Framework 的Token驗證功能:

api的通信采用token + ssl,簡化和方便線上腳本的調用。

Django版本1.8.16,djangorestframework版本3.5.3,

用了框架提供的rest_framework.authtoken.views.obtain_auth_token和rest_framework.authentication.TokenAuthentication后,

發現了一個問題,前者認證通過創建token后,這個token就不會自動更新了,非常不安全,非常危險。

后者驗證時候是不帶緩存的,需要查詢數據庫,由於每次請求都要驗證token,請求相當頻繁。

1、實現生成的token帶過期時間
首先在setting.py配置文件設置過期時間 REST_FRAMEWORK_TOKEN_EXPIRE_MINUTES,這里設置為60分鍾

#
REST_FRAMEWORK_TOKEN_EXPIRE_MINUTES = 60
#

 

 

 

2.setting.py同目錄文件view.py編輯一個視圖:ObtainExpiringAuthToken

#coding=utf8
import datetime
from django.conf import settings
from rest_framework import status
from rest_framework.response import Response
from rest_framework.authtoken.models import Token
from rest_framework.authtoken.views import ObtainAuthToken
 
EXPIRE_MINUTES = getattr(settings, 'REST_FRAMEWORK_TOKEN_EXPIRE_MINUTES', 1)
 
class ObtainExpiringAuthToken(ObtainAuthToken):
    """Create user token"""
    def post(self, request):
        serializer = self.serializer_class(data=request.data)
        if serializer.is_valid():
            token, created =  Token.objects.get_or_create(user=serializer.validated_data['user'])
 
            time_now = datetime.datetime.now()
 
            if created or token.created < time_now - datetime.timedelta(minutes=EXPIRE_MINUTES):
                # Update the created time of the token to keep it valid
                token.delete()
                token = Token.objects.create(user=serializer.validated_data['user'])
                token.created = time_now
                token.save()
 
            return Response({'token': token.key})
        return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
 
obtain_expiring_auth_token = ObtainExpiringAuthToken.as_view()

 

3.配置獲取token的url

url.py新增url用於生成用戶token

from .views import obtain_expiring_auth_token
 
urlpatterns += [
    #url(r'^api/token/', obtain_auth_token, name='api-token'),
    url(r'^api/token/', obtain_expiring_auth_token, name='api-token'),
]

 

4.獲取token測試

# 用curl測試接口 api/token/
curl -H "Content-Type: application/json" -X POST -d '{"username":"test","password":"test"}' http://127.0.0.1:9000/api/token/
# 返回
{"token":"6ff54785241f825846e4c5fca61cceb6be7f911e"}%

然后,然后這個生成token的接口就好了。目前還有一個問題,用戶就是生成一個token例如A,然后用戶再也不來請求這個接口生成token,

那么這個用戶的token A也會一直生效且不會被更新,那么要需要結合token驗證函數,來強制刪除用戶過期的token。

5.強制token過期時間

自定義token驗證,強制刪除過期的token,順便緩存下沒有過期的token
首先在setting.py文件新增全局認證類api.authentication.ExpiringTokenAuthentication替換默認的rest_framework.authentication.TokenAuthentication

# 
REST_FRAMEWORK = {
    'DEFAULT_AUTHENTICATION_CLASSES': [
        'rest_framework.authentication.BasicAuthentication',
        #'rest_framework.authentication.TokenAuthentication',  #enable Token authentication
        'api.authentication.ExpiringTokenAuthentication'
    ],
    'PAGE_SIZE': 10,

新建authentication.py文件,改文件在api這個目錄下面。

# 
#coding=utf8
import datetime
from django.conf import settings
from rest_framework.authentication import TokenAuthentication
from rest_framework import exceptions
from django.utils.translation import ugettext_lazy as _
 
from django.core.cache import cache
 
EXPIRE_MINUTES = getattr(settings, 'REST_FRAMEWORK_TOKEN_EXPIRE_MINUTES', 1)
 
class ExpiringTokenAuthentication(TokenAuthentication):
    """Set up token expired time"""
    def authenticate_credentials(self, key):
        # Search token in cache
        cache_user = cache.get(key)
        if cache_user:
            return (cache_user, key)
 
        model = self.get_model()
        try:
            token = model.objects.select_related('user').get(key=key)
        except model.DoesNotExist:
            raise exceptions.AuthenticationFailed(_('Invalid token.'))
 
        if not token.user.is_active:
            raise exceptions.AuthenticationFailed(_('User inactive or deleted.'))
 
        time_now = datetime.datetime.now()
 
        if token.created < time_now - datetime.timedelta(minutes=EXPIRE_MINUTES):
            token.delete()
            raise exceptions.AuthenticationFailed(_('Token has expired then delete.'))
 
        if token:
            # Cache token
            cache.set(key, token.user, EXPIRE_MINUTES * 60)
 
        return (token.user, token)
#

 

 

 

 

 

 

 

 

 

 

 

 

 

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM