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) #
posted on 2018-05-25 21:41 myworldworld 阅读(2242) 评论(0) 编辑 收藏 举报