在本教程中,你将学习如何使用Django REST框架(DRF)实现基于令牌的认证。 令牌认证的工作原理是用用户名和密码交换一个令牌,该令牌将被用于所有后续请求,以便在服务器端识别用户。
在客户端如何处理认证的具体细节取决于你所使用的技术/语言/框架的不同。客户端可以是一个使用iOS或Android的移动应用程序,可以是一个使用Python或C++的桌面应用程序。它可能是一个使用PHP或Ruby的Web应用程序。
但一旦你了解了整个过程,就会更容易为你的特定用例找到必要的资源和文档。
令牌认证适用于客户端-服务器应用程序,在那里令牌被安全地存储。你不应该暴露你的令牌,因为这将(某种程度上)等同于把你的用户名和密码交给别人。
设置REST API项目
那么,让我们从最开始的地方开始吧。安装Django和DRF:
pip install django
pip install djangorestframework
创建一个新的Django项目:
django-admin.py startproject myapi .
导航到myapi文件夹:
cd myapi
启动一个新的应用程序。我将把我的应用程序称为core:
django-admin.py startapp core
以下是你的项目结构应该是这样的:
myapi/
|-- core/
| |-- migrations/
| |-- __init__.py
| |-- admin.py
| |-- apps.py
| |-- models.py
| |-- tests.py
| +-- views.py
|-- __init__.py
|-- settings.py
|-- urls.py
+-- wsgi.py
manage.py
将core应用(你创建的)和rest_framework应用(你安装的)添加到INSTALLED_APPS ,在settings.py模块内。
myapi/settings.py
INSTALLED_APPS = [
# Django Apps
'django.contrib.admin',
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.staticfiles',
# Third-Party Apps
'rest_framework',
# Local Apps (Your project's apps)
'myapi.core',
]
回到项目根目录(即manage.py脚本所在的文件夹),并迁移数据库:
python manage.py migrate
让我们创建我们的第一个API视图,只是为了测试一下:
myapi/core/views.py
from rest_framework.views import APIView
from rest_framework.response import Response
class HelloView(APIView):
def get(self, request):
content = {'message': 'Hello, World!'}
return Response(content)
现在在urls.py模块中注册一个路径:
myapi/urls.py
from django.urls import path
from myapi.core import views
urlpatterns = [
path('hello/', views.HelloView.as_view(), name='hello'),
]
所以现在我们有一个只有一个端点的API/hello/ ,我们可以执行GET 请求。我们可以使用浏览器来消费这个端点,只要访问URLhttp://127.0.0.1:8000/hello/ :

我们也可以通过在querystring中传递format 参数来要求接收纯JSON数据的响应,如http://127.0.0.1:8000/hello/?format=json :

这两种方法都可以用来尝试DRF API,但有时命令行工具更方便,因为我们可以更容易地玩弄请求头文件。你可以使用cURL,它在所有主要的Linux/MacOS发行版上都广泛使用:
curl http://127.0.0.1:8000/hello/

但通常我更喜欢使用HTTPie,它是一个相当棒的Python命令行工具:
http http://127.0.0.1:8000/hello/

现在我们来保护这个API端点,这样我们就可以实现令牌认证:
myapi/core/views.py
from rest_framework.views import APIView
from rest_framework.response import Response
from rest_framework.permissions import IsAuthenticated # <-- Here
class HelloView(APIView):
permission_classes = (IsAuthenticated,) # <-- And here
def get(self, request):
content = {'message': 'Hello, World!'}
return Response(content)
再试着访问这个API端点:
http http://127.0.0.1:8000/hello/

现在我们得到一个HTTP 403 Forbidden错误。现在我们来实现令牌认证,这样我们就可以访问这个端点了。
实现令牌认证
我们需要在我们的settings.py模块中添加两个信息。首先将rest_framework.authtoken加入你的INSTALLED_APPS ,并将TokenAuthentication 加入到REST_FRAMEWORK :
myapi/settings.py
INSTALLED_APPS = [
# Django Apps
'django.contrib.admin',
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.staticfiles',
# Third-Party Apps
'rest_framework',
'rest_framework.authtoken', # <-- Here
# Local Apps (Your project's apps)
'myapi.core',
]
REST_FRAMEWORK = {
'DEFAULT_AUTHENTICATION_CLASSES': [
'rest_framework.authentication.TokenAuthentication', # <-- And here
],
}
迁移数据库,创建将存储认证令牌的表:
python manage.py migrate

现在我们需要一个用户账户。让我们使用manage.py 命令行工具创建一个:
python manage.py createsuperuser --username vitor --email vitor@example.com
最简单的方法是再次使用命令行工具来生成一个令牌,只是为了测试目的:
python manage.py drf_create_token vitor

这条信息,即随机字符串9054f7aa9305e012b3c2300408c3dfdf390fcddf ,是我们接下来要用来验证的。
但现在我们已经有了TokenAuthentication ,让我们试着再向我们的/hello/ 端点发出请求:
http http://127.0.0.1:8000/hello/

注意我们的API现在是如何向客户提供一些所需的认证方法的额外信息的。
所以,最后,让我们使用我们的令牌吧!
http http://127.0.0.1:8000/hello/ 'Authorization: Token 9054f7aa9305e012b3c2300408c3dfdf390fcddf'

这就差不多了。从现在开始,在所有的后续请求中,你应该包括头信息Authorization: Token 9054f7aa9305e012b3c2300408c3dfdf390fcddf 。
这个格式看起来很奇怪,而且通常是一个关于如何设置这个头的混乱点。这将取决于客户端和如何设置HTTP请求头。
例如,如果我们使用cURL,命令会是这样的:
curl http://127.0.0.1:8000/hello/ -H 'Authorization: Token 9054f7aa9305e012b3c2300408c3dfdf390fcddf'
或者如果是Python请求的调用:
import requests
url = 'http://127.0.0.1:8000/hello/'
headers = {'Authorization': 'Token 9054f7aa9305e012b3c2300408c3dfdf390fcddf'}
r = requests.get(url, headers=headers)
或者如果我们使用的是Angular,你可以实现一个HttpInterceptor ,并设置一个头:
import { Injectable } from '@angular/core';
import { HttpRequest, HttpHandler, HttpEvent, HttpInterceptor } from '@angular/common/http';
import { Observable } from 'rxjs';
@Injectable()
export class AuthInterceptor implements HttpInterceptor {
intercept(request: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
const user = JSON.parse(localStorage.getItem('user'));
if (user && user.token) {
request = request.clone({
setHeaders: {
Authorization: `Token ${user.accessToken}`
}
});
}
return next.handle(request);
}
}
用户请求一个令牌
DRF提供了一个端点,让用户使用他们的用户名和密码来请求认证令牌。
在urls.py模块中包含以下路由:
myapi/urls.py
from django.urls import path
from rest_framework.authtoken.views import obtain_auth_token # <-- Here
from myapi.core import views
urlpatterns = [
path('hello/', views.HelloView.as_view(), name='hello'),
path('api-token-auth/', obtain_auth_token, name='api_token_auth'), # <-- And here
]
因此,现在我们有了一个全新的API端点,它是/api-token-auth/ 。让我们先检查一下它:
http http://127.0.0.1:8000/api-token-auth/

它并不处理GET请求。基本上它只是一个接收带有用户名和密码的POST请求的视图。
让我们再试一下。
http post http://127.0.0.1:8000/api-token-auth/ username=vitor password=123

响应体是与这个特定用户相关的令牌。在这一点上,你存储这个令牌,并将其应用到未来的请求中。
然后,同样,你要向API发出POST请求的方式取决于你所使用的语言/框架。
如果这是一个Angular客户端,你可以将令牌存储在localStorage ,如果这是一个桌面CLI应用程序,你可以将其存储在用户主目录下的一个文本文件中的dot文件。
结论
希望本教程提供了一些关于令牌认证如何工作的见解。我将尝试跟进本教程,提供一些Angular应用程序、命令行应用程序和Web客户端的具体例子。
需要注意的是,默认的令牌实现有一些限制,比如每个用户只能有一个令牌,没有内置的方法来设置令牌的到期日。