django-restframework之认证组件进阶,设置token的缓存与过期时间

我在之前的文章给大家说过,身份认证的组件authentication_classes
并且做了一个简单的登录验证生成token,然后在authentication_classes中校验token,
返回用户对象和token值
我再把这段代码贴出来,给大家回顾一下,并做了一点小改动,添加了创建时间

class Login(APIView):
    def post(self,request):
        fields = {'username','password'}
        data_set = set(request.data.keys())
        info = {}
        if fields.issubset(data_set):
            for k in fields:
                info[k] = request.data[k]
        user_instance = User.objects.filter(**info).first()
        response = {}
        if user_instance:
            token = UserToken.objects.update_or_create(user=user_instance,defaults={
                'token':set_token(),
                'created_at':datetime.now()
            }) 
            response['token'] = token[0].token
        else:
            response['error'] = '错了错了'
        return JsonResponse(response)

后面的返回JSON数据,你用django的JsonResponse或者rest中的Response都行

token生成了,接下来要做的就是在获取接口数据的时候进行身份认证了,我们之前进行身份验证的代码也在之前的文章有写到过
这里我为了对比再贴出来一遍

class UserAuth(BaseAuthentication):
    def authenticate(self,request):
         token = request.query_params.get('token')
		 try:
		         token = UserToken.objects.get(token=token)
		         return token.user,token.token
		 except Exception:
		         raise APIException('认证不通过')

之前我们就是简单的做了一下验证访问用户是否携带token以及携带的token是否正确
但是当我们的用户表存在千万级别数量的用户时
我们对用户做认证时,每次都需要对数据库进行一次查询,这样的效率其实是非常低下的,而且我们也应该对token设置一个过期时间,为了安全,否则一旦token值被恶意获取,那我们的数据就永久的面临着危险
于是,今天我就简单的优化一下我们的用户身份认证类的写法

下面就给大家贴代码:

先是model

class User(models.Model):
    user_types = ((1,'NOMAL'),
                 (2,'SVIP'),
                 (3,'VVIP'))
    username = models.CharField('用户名',max_length=22)
    password = models.CharField('密码',max_length=32)
    user_type = models.SmallIntegerField('用户级别',choices=user_types,default=1)


class UserToken(models.Model):
    user = models.OneToOneField(User,related_name='user_token')
    token = models.CharField('用户token',max_length=200)
    created_at = models.DateTimeField('创建时间',auto_now_add=True)

再来序列化类:

class BookSerialize(serializers.ModelSerializer):
    class Meta:
        model = Book
        fields = ['title','price','publish','author','publish_company','author_list']
        extra_kwargs = {
            'publish':{'write_only':True},
            'author': {'write_only': True},
        }

    publish_company = serializers.CharField(read_only=True,source='publish.company',max_length=32)
    author_list = serializers.SerializerMethodField()

    def get_author_list(self,book_obj):
        author_list = list()
        for author in book_obj.author.all():
            author_list.append(author.name)
        return author_list

然后认证类:
认证类较之前做了比较大的改动
我将token的位置放置在了请求头中,
较cookie来讲相对安全
较session来讲对服务器没有压力

from datetime import datetime, timedelta
from django.core.cache import cache

class UserAuth(BaseAuthentication):

    def authenticate(self,request):
        token = request.META.get('HTTP_AUTHENTICATION')  #将token放入请求头中
        print(token)
        user = cache.get(token)
        if user:
            print('这是缓存')
            return user,token
        token_obj = UserToken.objects.filter(token=token).first()
        if not token_obj:
            raise APIException('认证失败')
        created_time = token_obj.created_at
        print('创建时间',created_time)
        now_time = datetime.now()
        print('现在的时间',now_time)
        delta = now_time - created_time
        print('过去多少天时间',delta)
        if delta < timedelta(seconds=5):
            remain_time = timedelta(weeks=2) - delta
            print('剩余时间',remain_time)
            cache.set(token_obj.token,token_obj.user,min(remain_time.total_seconds(),3600*24*7))
            print('这是数据库')
            return token_obj.user,token
        else:
            raise APIException('认证超时')

最后视图接口:

class BookView(viewsets.ModelViewSet):
    authentication_classes = [UserAuth]
    queryset = Book.objects.all()
    serializer_class = BookSerialize

好了,我之前登录访问login接口生成了token,现在在请求头中携带token访问数据
我在认证类中,设置的过期时间为5秒,因为token的值我肯定是在5秒前就生成好了,
所以在请求数据的时候,会告诉我认证超时,我们看看结果:
在这里插入图片描述

现在,我更改一下token的过期时间,设置为两周

if delta < timedelta(weeks=2):

我们再看一下结果:
在这里插入图片描述
成功请求数据

name缓存呢?我想设置7天的缓存时间,但是我的缓存时间为什么不直接写

 cache.set(token_obj.token,token_obj.user,3600*24*7)

而是要写成:

cache.set(token_obj.token,token_obj.user,min(remain_time.total_seconds(),3600*24*7))

试想一下,当一个用户第一天登录,我们设置了7天的缓存,这7天内,用户的每次数据获取的身份验证都是从缓存中取验证信息
但7天过了之后,用户没有再登录,而是在第十天登录,我们再次从数据库查询,又设置了一次7天的缓存,而token的过期时间为两周14天,这样就导致token过期了,但用户依旧可以通过认证获取数据
所以,我们需要进行这方面的处理

来看一下设置了缓存后的运行效果
在这里插入图片描述
当第一次请求的数据进行验证的时候,我们经过的是数据库
但第二次看是,我们经过的就是缓存了
如此一来,认证效率会大大提高

当然,我这里也只是简单的做了一下认证
有许多功能已经非常完善的认证工具库我们拿过来直接用就可以了,比如jsonwebtoken
写这些是为了大家理解token认证的这个过程

好了,关于认证优化的分享目前就到这里了

  • 1
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值