【Tay的临时收藏】

适合小白的Django rest_framework Token

2019-08-05  本文已影响12人  山野过客

出自:https://blog.csdn.net/yueguangMaNong/article/details/90519819

前言

我为什么要写这篇文章?

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

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

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

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

网上文章虽然多,但多数是大神的教程,神龙见首不见尾,原理一讲,一段关键代码贴上来就完事了,对我这样的学渣实在是不怎么友好。

做个学习记录,给后来者一个方便

开始

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

创建一个 Django项目,新建一个app(废话)

安装 django-rest-framework (还是废话)

在settings中注册rest-framework,配置 REST_FRAMEWORK

编写自己的验证方法,并加入REST_FRAMEWORK配置

在登陆视图中创建 Token

在操作视图中添加 Toekn 验证

使用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})

说明一下:

装饰器api.view是rest framework提供的,它可以指定请求方法,如果这里使用了它,那么针对这个方法的CSRF就会失效,所以不需要再额外添加 csrf_exempt 装饰器

这个装饰器会给request对象添加一个data属性,post上传的数据都可以在这里面直接获取,示例中用户名和密码就是直接从里面取出来的,上传的json数据已经转换好了,直接获取即可。

auth.authenticate() 方法是Django提供的验证用户名和密码是否匹配的,和rest framework毛关系都没有

创建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": "验证失败"})

说明:

request.user.is_authenticated:这个是验证Token是否正确的。user对象也是使用了api_view装饰器之后添加的。

很明显,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已经失效了,尴尬。来个正确的打开方式

成功的示范

注意几点:

headers里面的key只能是【Authorization】,别的名称将会验证失败,至于能不能,应该是可以的,只是我不知道(手动狗头)

token一定要在有效期内才能验证通过(废话),时间在auth.py文件里面设置,注意时区的问题。自己试验,建议设置USE_TZ = False,简单粗暴。

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

上一篇下一篇

猜你喜欢

热点阅读