【DRF】django restframework如何使用redis来做token认证

本文涉及的产品
Redis 开源版,标准版 2GB
推荐场景:
搭建游戏排行榜
云数据库 Tair(兼容Redis),内存型 2GB
简介: 【DRF】django restframework如何使用redis来做token认证

一、前言


restframework有自己很方便的一套认证、权限体系:官方文档(tokenauthentication)

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

1251e58d24fb46fcaa0c51e040d71c17.png


有时候在后续接口中需要使用的用户信息过多时,频繁、高并发下的查询数据库会带来比较大的性能消耗。这个时候我们就需要通过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的初始化

e6493d66ee0b4f88b5971dbfafb804f2.png

加入下列代码


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存储的用户记录

269c1cfd5bed43488d9c574ad599f1a3.png

4. 重写认证token方法


4.1 源码分析

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

baed8878ad8f4da8bd6a8c0d0658b056.png

这个类中我们只需要关注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):
        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的团队点个赞!!!

相关实践学习
基于Redis实现在线游戏积分排行榜
本场景将介绍如何基于Redis数据库实现在线游戏中的游戏玩家积分排行榜功能。
云数据库 Redis 版使用教程
云数据库Redis版是兼容Redis协议标准的、提供持久化的内存数据库服务,基于高可靠双机热备架构及可无缝扩展的集群架构,满足高读写性能场景及容量需弹性变配的业务需求。 产品详情:https://www.aliyun.com/product/kvstore     ------------------------------------------------------------------------- 阿里云数据库体验:数据库上云实战 开发者云会免费提供一台带自建MySQL的源数据库 ECS 实例和一台目标数据库 RDS实例。跟着指引,您可以一步步实现将ECS自建数据库迁移到目标数据库RDS。 点击下方链接,领取免费ECS&RDS资源,30分钟完成数据库上云实战!https://developer.aliyun.com/adc/scenario/51eefbd1894e42f6bb9acacadd3f9121?spm=a2c6h.13788135.J_3257954370.9.4ba85f24utseFl
目录
相关文章
|
2月前
|
存储 缓存 NoSQL
深入理解Django与Redis的集成实践
深入理解Django与Redis的集成实践
62 0
|
前端开发 API 数据格式
Django的restframework接口框架自定义返回数据格式
在前后端分离是大趋势的背景下,前端获取数据都是通过调用后台的接口来获取数据微服务的应用越来越多。Django是Python进行web应用开发常用的web框架,用Django框架进行web应用框架减少了很多工作,通常用很少量的代码就可以实现数据的增、删、改、查的业务应用,同样用Django的restframework的框架对外发布接口也是非常的简单方便,几行代码就可以将数据对象通过接口的方式提供服务。因为在实际开发过程中接口的返回数据有一定的格式,本文介绍通过自定义Response返回对象来自定义接口返回数据格式。
198 1
|
7月前
|
存储 NoSQL 前端开发
jwt与redis,把生成的token放入redis中进行临时存储
jwt与redis,把生成的token放入redis中进行临时存储
443 0
|
2月前
|
NoSQL Java Redis
shiro学习四:使用springboot整合shiro,正常的企业级后端开发shiro认证鉴权流程。使用redis做token的过滤。md5做密码的加密。
这篇文章介绍了如何使用Spring Boot整合Apache Shiro框架进行后端开发,包括认证和授权流程,并使用Redis存储Token以及MD5加密用户密码。
31 0
shiro学习四:使用springboot整合shiro,正常的企业级后端开发shiro认证鉴权流程。使用redis做token的过滤。md5做密码的加密。
|
4月前
|
安全 数据库 数据安全/隐私保护
|
4月前
|
中间件 API 网络架构
Django后端架构开发:从匿名用户API节流到REST自定义认证
Django后端架构开发:从匿名用户API节流到REST自定义认证
46 0
|
6月前
|
安全 API 数据安全/隐私保护
Django REST framework安全实践:轻松实现认证、权限与限流功能
Django REST framework安全实践:轻松实现认证、权限与限流功能
|
6月前
|
NoSQL 安全 Linux
springboot+shiro+redis前后端分离实现认证(一)
springboot+shiro+redis前后端分离实现认证(一)
233 0
【Django学习】(十六)session_token认证过程与区别_响应定制
【Django学习】(十六)session_token认证过程与区别_响应定制