我为什么要写这篇文章?
最近由于工作需要,在原来的项目(Django,B/S)的基础上需要增加和客户端(Client)的通信。这个时候session机制就不合适了,需要用到token。我选择了Rest Framework 插件来完成这个功能。关于django token的教程,网上一搜一大堆,我为什么还要写?主要由以下几点:
饮水思源:写这篇文章之前,我被各种bug折磨了一天,借鉴了两位大佬的东西,欢迎去他们下面查看原文。
https://www.jianshu.com/p/e0a206212df4
https://www.jianshu.com/p/078fb116236e
整个过程总共分为以下几步
我相信搜索到这篇文章的都不是0基础,起码创建项目,新增一个app是没问题的吧。我就略过这步了。如果有需要指导创建项目,新增app的,请留言!
> pip install django-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首先要有用户,我这里直接使用系统的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(['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": "验证失败"})
说明:
from django.urls import path
from . import views
urlpatterns = [
path('login/', views.login),
path('doSomething/', views.do_something),
]
很简单,就是简单的登陆和操作验证两个url
功能都已经完成了,就是这么简单,最后我们来测试一下
先创建一个用户:
用户名:zhangsan 密码: 123456
然后使用Postman登陆获取Token
可以看到,一切都那么自然,成功获取到了Token
接下来就来测试一下这个token的正确性
过期的情况由于之前时间设置得太短,导致写完文字后token已经失效了,尴尬。来个正确的打开方式
成功的示范注意几点:
OK,大功告成,希望这篇文章能够帮你,欢迎留言回复。