适合小白的Django rest_framework Token入门

前言

我为什么要写这篇文章?

最近由于工作需要,在原来的项目(Django,B/S)的基础上需要增加和客户端(Client)的通信。这个时候session机制就不合适了,需要用到token。我选择了Rest Framework 插件来完成这个功能。关于django token的教程,网上一搜一大堆,我为什么还要写?主要由以下几点:

 

饮水思源:写这篇文章之前,我被各种bug折磨了一天,借鉴了两位大佬的东西,欢迎去他们下面查看原文。

https://www.jianshu.com/p/e0a206212df4

https://www.jianshu.com/p/078fb116236e 

 

  1. 网上文章虽然多,但多数是大神的教程,神龙见首不见尾,原理一讲,一段关键代码贴上来就完事了,对我这样的学渣实在是不怎么友好。
  2. 做个学习记录,给后来者一个方便

开始

整个过程总共分为以下几步

  1. 创建一个 Django项目,新建一个app(废话)
  2. 安装 django-rest-framework (还是废话)
  3. 在settings中注册rest-framework,配置 REST_FRAMEWORK
  4. 编写自己的验证方法,并加入REST_FRAMEWORK配置
  5. 在登陆视图中创建 Token
  6. 在操作视图中添加 Toekn 验证
  7. 使用postman 验证

创建Django项目

我相信搜索到这篇文章的都不是0基础,起码创建项目,新增一个app是没问题的吧。我就略过这步了。如果有需要指导创建项目,新增app的,请留言!

 

安装 REST_FRAMEWORK

> pip install django-rest-framework

 

注册 rest-framework并配置

不需要导入这个包,直接注册即可

INSTALLED_APPS = [
    ...
    'rest_framework.authtoken'
    ...
]

配置一下, 这个是必须的,不然返回数据的时候回报错

REST_FRAMEWORK = {
    'DEFAULT_RENDERER_CLASSES': (
        'rest_framework.renderers.JSONRenderer',
        'rest_framework.renderers.BrowsableAPIRenderer',
    ),
}

 

编写我们的验证方法

在app下面创建一个名为auth.py的文件,将下面的内容写入这个文件中

import datetime

from django.utils.translation import ugettext_lazy
from django.core.cache import cache
from rest_framework.authentication import BaseAuthentication
from rest_framework import exceptions
from rest_framework.authtoken.models import Token
from rest_framework import HTTP_HEADER_ENCODING


# 获取请求头信息
def get_authorization_header(request):
    auth = request.META.get('HTTP_AUTHORIZATION', b'')
    if isinstance(auth, type('')):
        auth = auth.encode(HTTP_HEADER_ENCODING)
    return auth


# 自定义认证方式,这个是后面要添加到设置文件的
class ExpiringTokenAuthentication(BaseAuthentication):
    model = Token

    def authenticate(self, request):
        auth = get_authorization_header(request)
        if not auth:
            return None
        try:
            token = auth.decode()
        except UnicodeError:
            msg = ugettext_lazy("无效的Token, Token头不应包含无效字符")
            raise exceptions.AuthenticationFailed(msg)

        return self.authenticate_credentials(token)

    def authenticate_credentials(self, key):
        # 尝试从缓存获取用户信息(设置中配置了缓存的可以添加,不加也不影响正常功能)
        token_cache = 'token_' + key
        cache_user = cache.get(token_cache)
        if cache_user:
            return cache_user, cache_user   # 这里需要返回一个列表或元组,原因不详
        # 缓存获取到此为止

        # 下面开始获取请求信息进行验证
        try:
            token = self.model.objects.get(key=key)
        except self.model.DoesNotExist:
            raise exceptions.AuthenticationFailed("认证失败")

        if not token.user.is_active:
            raise exceptions.AuthenticationFailed("用户被禁用")

        # Token有效期时间判断(注意时间时区问题)
        # 我在设置里面设置了时区 USE_TZ = False,如果使用utc这里需要改变。
        if (datetime.datetime.now() - token.created) > datetime.timedelta(hours=0.1*1):
            raise exceptions.AuthenticationFailed('认证信息已过期')

        # 加入缓存增加查询速度,下面和上面是配套的,上面没有从缓存中读取,这里就不用保存到缓存中了
        if token:
            token_cache = 'token_' + key
            cache.set(token_cache, token.user, 600)
        
        # 返回用户信息
        return token.user, token

    def authenticate_header(self, request):
        return 'Token'

将这个验证方法注册到配置里面

REST_FRAMEWORK = {
    # 新增的
    'DEFAULT_AUTHENTICATION_CLASSES': (
        'apps.xtauth.auth.ExpiringTokenAuthentication',   # 根据自己的实际情况填写路径
    ),
    # 下面是刚刚已经写好的
    'DEFAULT_RENDERER_CLASSES': (
        'rest_framework.renderers.JSONRenderer',
        'rest_framework.renderers.BrowsableAPIRenderer',
    ),
}

一切准备工作就绪,下面就开始使用它了。

 

创建Token

创建token首先要有用户,我这里直接使用系统的User表,就不直接创建了

下面的代码写在视图views.py

from django.http import JsonResponse
from django.shortcuts import HttpResponse
from rest_framework.decorators import api_view
from rest_framework.authtoken.models import Token
from django.contrib import auth


@api_view(['POST'])
def login(request):
    receive = request.data
    username = receive.get('username')
    password = receive.get('password')
    user = auth.authenticate(username=username, password=password)
    if not user:
        return HttpResponse("用户名和密码不匹配")
    # 校验通过
    # 删除原有的Token
    old_token = Token.objects.filter(user=user)
    old_token.delete()
    # 创建新的Token
    token = Token.objects.create(user=user)
    return JsonResponse({"username": user.username, "token": token.key})

说明一下:

  1. 装饰器api.view是rest framework提供的,它可以指定请求方法,如果这里使用了它,那么针对这个方法的CSRF就会失效,所以不需要再额外添加 csrf_exempt 装饰器
  2. 这个装饰器会给request对象添加一个data属性,post上传的数据都可以在这里面直接获取,示例中用户名和密码就是直接从里面取出来的,上传的json数据已经转换好了,直接获取即可。
  3. auth.authenticate() 方法是Django提供的验证用户名和密码是否匹配的,和rest framework毛关系都没有
  4. 创建token是通过Token.objects.create() 创建的。创建之前最好先删除原来的token,否则一个用户多次创建token会报错的,数据库唯一约束

 

验证Token

创建好了之后,自然就该验证了,写一个什么函数验证一下

@api_view(['POST'])
def do_something(request):
    receive = request.data
    print(receive)
    if request.user.is_authenticated:   # 验证Token是否正确
        print("Do something...")
        return JsonResponse({"msg": "验证通过"})
    else:
        print("验证失败")
        return JsonResponse({"msg": "验证失败"})

说明:

  1. request.user.is_authenticated:这个是验证Token是否正确的。user对象也是使用了api_view装饰器之后添加的。
  2. 很明显,rest framework, 我们应该遵循RestFul规范,返回json格式数据才是中规中矩的。如果起前面的设置中REST_FRAMEWORK下面没有配置 rest_framework.renderers.JSONRenderer 的话,这里会报错,一定要添加

 

路由配置

from django.urls import path

from . import views

urlpatterns = [
    path('login/', views.login),
    path('doSomething/', views.do_something),
]

很简单,就是简单的登陆和操作验证两个url

 

Postman测试

功能都已经完成了,就是这么简单,最后我们来测试一下

先创建一个用户:

用户名:zhangsan       密码: 123456

然后使用Postman登陆获取Token

可以看到,一切都那么自然,成功获取到了Token

接下来就来测试一下这个token的正确性

过期的情况
过期的情况

由于之前时间设置得太短,导致写完文字后token已经失效了,尴尬。来个正确的打开方式

成功
成功的示范

注意几点:

  1. headers里面的key只能是【Authorization】,别的名称将会验证失败,至于能不能,应该是可以的,只是我不知道(手动狗头)
  2. token一定要在有效期内才能验证通过(废话),时间在auth.py文件里面设置,注意时区的问题。自己试验,建议设置USE_TZ = False,简单粗暴。

 

OK,大功告成,希望这篇文章能够帮你,欢迎留言回复。

 

 

 

 

 

 

 

 

  • 10
    点赞
  • 30
    收藏
    觉得还不错? 一键收藏
  • 19
    评论
评论 19
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值