目录

    一、前言

    restframework有自己很方便的一套认证、权限体系:

    官方文档的token 是基于数据库中的authtoken_token表来做的

    有时候在后续接口中需要使用的用户信息过多时,频繁、高并发下的查询数据库会带来比较大的性能消耗。这个时候我们就需要通过redis来做用户认证,并存储一些用户信息在其中。下面就为你讲解如何基于redis来使用drf做用户认证。

    二、详解

    1. 前期准备

    1.1 安装redis并启动

    自行安装!这个都装不好后面的教程也不用看了!看了也理解不了!

    1.2 安装django-redis库

    pip install django-redis -i https://pypi.tuna.tsinghua.edu.cn/simple

    2. 配置redis

    2.1 配置redis连接

    settings.py输入下面的代码 (我的redis没有设置密码,所以配置代码中无密码相关配置)

    # redis缓存配置
    caches = {
        "default": {
            "backend": "django_redis.cache.rediscache",
            "location": "redis://127.0.0.1:6379/",
            "options": {
                "client_class": "django_redis.client.defaultclient",
                "compressor": "django_redis.compressors.zlib.zlibcompressor",
                "ignore_exceptions": true,
            }
        }
    }

    2.2 初始化redis连接

    在项目初始化文件中(__init__.py)加入下面代码进行redis的初始化

    加入下列代码

    from django_redis import get_redis_connection
    redis_connect = get_redis_connection()
    

    3. 将token写入redis

    在之前的登录接口是将token写入数据库的,现在需要重写它让其写入redis

    3.1 原来的登录代码

    @api_view(['post'])
    def login(request):
        """
        登录接口
        """
        user = authenticate(username=request.data['username'], password=request.data['password'])
        if user:
            token.objects.filter(user_id=user.id).delete()
            token = token.objects.create(user=user)
            _dict = {'id': user.id, 'username': user.username, 'first_name': user.first_name,
                     'last_name': user.last_name, 'email': user.email}        
            redis_connect.set(token.key, json.dumps(_dict), 259200)  # 存redis 259200秒=72个小时
            return response(data={'status_code': 200, 'msg': '登录成功!', 'token': token.key})
        return response(data={'status_code': 403, 'msg': '密码错误!'})
    

    3.2 重写后的登录代码

    @api_view(['post'])
    def login(request):
        """
        登录接口
        """
        user = authenticate(username=request.data['username'], password=request.data['password'])
        if user:
            token = binascii.hexlify(os.urandom(20)).decode()  # 生成token 的方式
            _dict = {'id': user.id, 'username': user.username, 'first_name': user.first_name,
                     'last_name': user.last_name, 'email': user.email}
            redis_connect.set(token, json.dumps(_dict), 259200)  # 存redis 259200秒=72个小时
            return response(data={'status_code': 200, 'msg': '登录成功!', 'token': token})
        return response(data={'status_code': 403, 'msg': '密码错误!'})
    

    3.3 登录后redis存储的用户记录

    4. 重写认证token方法

    4.1 源码分析

    我们可以全局搜索tokenauthentication 找到【restframework】源码中的token认证类

    这个类中我们只需要关注authenticate_credentials这个方法就可以了。

        def authenticate_credentials(self, 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.'))
    
            return (token.user, token)
    

    源码首先通过接口请求的token (源码中的key) 去数据库中寻找是否有该对应的记录
    如果有则认证成功返回usertoken这两个模型对象

    如果没有对应的记录,则抛出【invalid token】异常

            try:
                token = model.objects.select_related('user').get(key=key)
            except model.doesnotexist:
                raise exceptions.authenticationfailed(_('invalid token.'))
    

    如果有对应的记录,但用户是未激活的 (is_active=0) 则抛出【user inactive or deleted】异常

          if not token.user.is_active:
                raise exceptions.authenticationfailed(_('user inactive or deleted.'))
    

    然后restframework会在视图层的dispatch方法中进行异常的封装并返回响应结果。

    4.2 进行重写

    经过源码分析,我们需要重写的有两部分:

    1.验证token (源码中的key) 是否有效,之前是从数据库进行验证的现在需要通过redis去验证

    2.重新封装user模型对象,但有个问题需要注意的是:
    如果你重写了django的user对象,让它关联了其他表的属性,那么这里则不能将其封装进user这个对象的,因为redis不能存储一个对象!,当然如果非要这么做可以将外键id值在登录 (写入token) 的时候存入redis,然后在这里通过该外键id去查询关联的外键表获取属性,再封装到user模型对象中!

    重写后的代码

    class redistokenauthentication(tokenauthentication):
    
        def authenticate_credentials(self, key):
            null = none  # json的none为null,所以需要定义一下
            user_data = redis_connect.get(key)
            if user_data:
                user_dict = json.loads(user_data)
                user_obj = user()
                for key_name in user_dict.keys():
                    setattr(user_obj, key_name, user_dict[key_name])
                return user_obj, key
            raise exceptions.authenticationfailed(_('无效的token.'))
    

    4.3 加入认证配置

    settings.py配置文件中,加入如下配置

    rest_framework = {
        'default_authentication_classes': (  #如果有rest_framework配置项了单独加入该项即可
            'demo.redisauthentication.redistokenauthentication',  # 项目名称.重新认证类所在的文件.类名
        ),
    }
    

    4.4 效果展示

    增加一个接口

    path('test-token', views.test_token),
    

    接口方法代码

    @api_view(['get'])
    @permission_classes((permissions.isauthenticated,))
    def test_token(request):
        """
        测试token
        """
        print('登录的用户名是:', request.user)
        res_data = {'data': {'status_code': 200}}
        return response(**res_data)
    

    输出结果

    登录的用户名是: admin

    三、总结

    无论是django还是restframework,他们的源码和官方文档在我看来是非常清晰的 (在我看的那么多官方文档中算很清晰的了) 这里给drf的团队点个赞!!!

    到此这篇关于django restframework使用redis实现token认证的文章就介绍到这了,更多相关django restframework token认证内容请搜索www.887551.com以前的文章或继续浏览下面的相关文章希望大家以后多多支持www.887551.com!