JWT是JSON Web Token的缩写,它是客户端/服务器应用程序使用的一种认证策略,其中客户端是使用JavaScript和一些前端框架(如Angular、React或VueJS)的Web应用程序。
在本教程中,我们将探讨JWT认证的具体内容。如果你想了解更多关于使用Django REST框架(DRF)的基于令牌的认证,或者你想知道如何开始一个新的DRF项目,你可以阅读本教程。这些概念都是一样的,我们只是要换一个认证后端。
JWT如何工作?
JWT只是一个授权令牌,应包含在所有请求中:
curl http://127.0.0.1:8000/hello/ -H 'Authorization: Bearer eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ0b2tlbl90eXBlIjoiYWNjZXNzIiwiZXhwIjoxNTQzODI4NDMxLCJqdGkiOiI3ZjU5OTdiNzE1MGQ0NjU3OWRjMmI0OTE2NzA5N2U3YiIsInVzZXJfaWQiOjF9.Ju70kdcaHKn1Qaz8H42zrOYk0Jx9kIckTn9Xx7vhikY'
JWT是通过将用户名+密码与访问令牌和刷新令牌交换而获得的。
访问令牌通常是短暂的(在5分钟左右到期,但可以定制)。
刷新令牌的寿命稍长一些(24小时内到期,也可定制)。它相当于一个认证会话。过期后,你需要再次用用户名+密码完全登录。
这是为什么呢?
这是一个安全功能,也是因为JWT拥有更多的信息。如果你仔细看我上面给出的例子,你会看到令牌由三个部分组成。
xxxxx.yyyyy.zzzzz
这是组成JWT的三个独特的部分:
header.payload.signature
所以我们这里有:
header = eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9
payload = eyJ0b2tlbl90eXBlIjoiYWNjZXNzIiwiZXhwIjoxNTQzODI4NDMxLCJqdGkiOiI3ZjU5OTdiNzE1MGQ0NjU3OWRjMmI0OTE2NzA5N2U3YiIsInVzZXJfaWQiOjF9
signature = Ju70kdcaHKn1Qaz8H42zrOYk0Jx9kIckTn9Xx7vhikY
这些信息是用Base64编码的。如果我们进行解码,我们会看到这样的东西:
头部
{
"typ": "JWT",
"alg": "HS256"
}
有效载荷
{
"token_type": "access",
"exp": 1543828431,
"jti": "7f5997b7150d46579dc2b49167097e7b",
"user_id": 1
}
签名
签名是由JWT后端发出的,使用头base64 + payload base64 +SECRET_KEY 。在每次请求时,这个签名都会被验证。如果头或有效载荷中的任何信息被客户端改变,它将使签名无效。检查和验证签名的唯一方法是通过使用你的应用程序的SECRET_KEY 。在其他方面,这就是为什么你应该始终保持你的SECRET_KEY 的秘密!
安装和设置
在本教程中,我们将使用 djangorestframework_simplejwt 库,该库由DRF开发者推荐。
pip install djangorestframework_simplejwt
设置.py
REST_FRAMEWORK = {
'DEFAULT_AUTHENTICATION_CLASSES': [
'rest_framework_simplejwt.authentication.JWTAuthentication',
],
}
urls.py
from django.urls import path
from rest_framework_simplejwt import views as jwt_views
urlpatterns = [
# Your URLs...
path('api/token/', jwt_views.TokenObtainPairView.as_view(), name='token_obtain_pair'),
path('api/token/refresh/', jwt_views.TokenRefreshView.as_view(), name='token_refresh'),
]
示例代码
在本教程中,我将使用以下路由和API视图:
views.py
from rest_framework.views import APIView
from rest_framework.response import Response
from rest_framework.permissions import IsAuthenticated
class HelloView(APIView):
permission_classes = (IsAuthenticated,)
def get(self, request):
content = {'message': 'Hello, World!'}
return Response(content)
urls.py
from django.urls import path
from myapi.core import views
urlpatterns = [
path('hello/', views.HelloView.as_view(), name='hello'),
]
使用方法
我将使用HTTPie来通过终端消费API端点。但你也可以使用cURL(在许多操作系统中都可以使用)在本地进行尝试。
或者使用DRF的Web界面,像这样访问端点的URL:

获取令牌
第一步是认证和获取令牌。该端点是/api/token/ ,它只接受POST请求:
http post http://127.0.0.1:8000/api/token/ username=vitor password=123

所以基本上你的响应体就是两个令牌:
{
"access": "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ0b2tlbl90eXBlIjoiYWNjZXNzIiwiZXhwIjoxNTQ1MjI0MjU5LCJqdGkiOiIyYmQ1NjI3MmIzYjI0YjNmOGI1MjJlNThjMzdjMTdlMSIsInVzZXJfaWQiOjF9.D92tTuVi_YcNkJtiLGHtcn6tBcxLCBxz9FKD3qzhUg8",
"refresh": "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ0b2tlbl90eXBlIjoicmVmcmVzaCIsImV4cCI6MTU0NTMxMDM1OSwianRpIjoiMjk2ZDc1ZDA3Nzc2NDE0ZjkxYjhiOTY4MzI4NGRmOTUiLCJ1c2VyX2lkIjoxfQ.rA-mnGRg71NEW_ga0sJoaMODS5ABjE5HnxJDb0F8xAo"
}
之后,你要在客户端存储访问令牌和刷新令牌,通常是在localStorage中。
为了访问后端受保护的视图(即需要认证的API端点),你应该在所有请求的头中包含访问令牌,像这样:
http http://127.0.0.1:8000/hello/ "Authorization: Bearer eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ0b2tlbl90eXBlIjoiYWNjZXNzIiwiZXhwIjoxNTQ1MjI0MjAwLCJqdGkiOiJlMGQxZDY2MjE5ODc0ZTY3OWY0NjM0ZWU2NTQ2YTIwMCIsInVzZXJfaWQiOjF9.9eHat3CvRQYnb5EdcgYFzUyMobXzxlAVh_IAgqyvzCE"

你可以在未来5分钟内使用这个访问令牌。
五分钟后,该令牌将过期,如果你试图再次访问该视图,你将会得到以下错误。
http http://127.0.0.1:8000/hello/ "Authorization: Bearer eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ0b2tlbl90eXBlIjoiYWNjZXNzIiwiZXhwIjoxNTQ1MjI0MjAwLCJqdGkiOiJlMGQxZDY2MjE5ODc0ZTY3OWY0NjM0ZWU2NTQ2YTIwMCIsInVzZXJfaWQiOjF9.9eHat3CvRQYnb5EdcgYFzUyMobXzxlAVh_IAgqyvzCE"

刷新令牌
要获得一个新的访问令牌,你应该使用刷新令牌端点/api/token/refresh/ ,发布刷新令牌。
http post http://127.0.0.1:8000/api/token/refresh/ refresh=eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ0b2tlbl90eXBlIjoicmVmcmVzaCIsImV4cCI6MTU0NTMwODIyMiwianRpIjoiNzAyOGFlNjc0ZTdjNDZlMDlmMzUwYjg3MjU1NGUxODQiLCJ1c2VyX2lkIjoxfQ.Md8AO3dDrQBvWYWeZsd_A1J39z6b6HEwWIUZ7ilOiPE

返回的是一个新的访问令牌,你应该在随后的请求中使用。
刷新令牌在未来24小时内有效。当它最终也过期时,用户将需要使用他们的用户名和密码再次进行完整的认证,以获得一套新的访问令牌+刷新令牌。
刷新令牌的意义何在?
乍一看,刷新令牌可能看起来毫无意义,但事实上它是必要的,以确保用户仍然有正确的权限。如果你的访问令牌有很长的过期时间,更新与令牌相关的信息可能需要更长时间。这是因为认证检查是通过加密手段完成的,而不是通过查询数据库和验证数据完成的。所以有些信息是被缓存的。
还有一个安全方面,从某种意义上说,刷新令牌只在POST数据中旅行。而访问令牌是通过HTTP头发送的,可能会在途中被记录下来。因此,这也给了一个短暂的窗口,如果你的访问令牌被破坏。