优化Django Rest Framework 的Token验证功能

pi的通信采用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
#

setting.py同目录文件view.py编辑一个视图

#
#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()
#

url.py新增url用于生成用户token

#
#from rest_framework.authtoken.views import obtain_auth_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' ),
]
#

用curl测试接口 api/token/

#
git:(master) ✗ 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。

2、自定义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)
#

然后然后,所有的功能都实现了,删除用户过期的token和缓存token减少数据库查询。

 

你可能感兴趣的:(python&nodejs)