本教程将介绍 JSON Web Tokens (JWT) 以及如何在 Django 中实现 JWT 身份验证。
什么是JWT?
JWT 是一个编码的 JSON 字符串,它在标头中传递以对请求进行身份验证。它通常是通过使用密钥对 JSON 数据进行哈希处理而获得的。这意味着服务器不需要每次都查询数据库来检索与给定令牌关联的用户。
JSON Web 令牌如何工作
当用户使用其凭据成功登录时,将获取一个 JSON Web Token 并将其保存在本地存储中。每当用户想要访问受保护的 URL 时,都会在请求的标头中发送令牌。然后服务器检查授权标头中的有效 JWT,如果找到,将允许用户访问。
典型的内容标题如下所示:
Authorization: Bearer eyJhbGciOiJIUzI1NiIsI
下图展示了这个过程:
认证和授权的概念
认证是识别登录用户的过程,而授权是识别某个用户是否有权访问网络资源的过程。
api范例
在本教程中,我们将在 Django JWT 中构建一个简单的用户身份验证系统作为身份验证机制。
要求
Django
让我们开始吧。
创建一个目录,您将在其中保存您的项目以及一个用于安装项目依赖项的虚拟环境。
mkdir myprojects cd myprojects virtual ven
激活虚拟环境:
source venv/bin/activate
创建 Django 项目。
django-admin startproject django_auth
使用 pip 安装 DRF 和 django-rest-framework-jwt。
pip install djangorestframework pip install djangorestframework-jwt pip install django
让我们继续将 DRF 添加到 settings.py文件中已安装应用程序的列表中。
配置 JWT 设置
为了使用简单的 JWT,我们需要配置 django-rest-framework 权限来接受 JSON Web Tokens。
在该 settings.py文件中,添加以下配置:
REST_FRAMEWORK = { 'DEFAULT_AUTHENTICATION_CLASSES': ( 'rest_framework_jwt.authentication.JSONWebTokenAuthentication', ), }
创建一个名为 users 的新应用程序,它将处理用户身份验证和管理。
cd django-auth django-admin.py startapp users
将用户应用程序添加到文件中已安装应用程序的列表中settings.py。
设置数据库
我们将使用 postgresql 数据库,因为它更稳定、更健壮。
创建 auth 数据库并分配用户。
通过键入以下内容切换到您机器上的 Postgres 帐户:
sudo su postgres
访问 Postgres 提示并创建数据库:
psql postgres=# CREATE dataBASE auth;
创建角色:
postgres = # CREATE ROLE django_auth WITH LOGIN PASSWORD 'asdfgh';
授予用户数据库访问权限:
postgres = # GRANT ALL PRIVILEGES ON DATABASE auth TO django_auth;
安装 psycopg2 包,这将允许我们使用我们配置的数据库:
pip install psycopg2
编辑当前配置的 sqlite 数据库,使用 Postgres 数据库。
DATABASES = { 'default': { 'ENGINE': 'django.db.backends.postgresql_psycopg2', 'NAME': 'auth', 'USER': 'django_auth', 'PASSWORD': 'asdfgh', 'HOST': 'localhost', 'PORT': '', } }
创建模型
Django 自带了一个非常精细的内置认证系统,但有时我们需要进行调整,因此我们需要创建一个自定义的用户认证系统。我们的用户模型将继承自AbstractBaseUser提供的类django.contrib.auth.models。
在 users/models.py 中,我们首先创建用户模型来存储用户详细信息。
# users/models.py from __future__ import unicode_literals from django.db import models from django.utils import timezone from django.contrib.auth.models import ( AbstractBaseUser, PermissionsMixin ) class User(AbstractBaseUser, PermissionsMixin): """ An abstract base class implementing a fully featured User model with admin-compliant permissions. """ email = models.EmailField(max_length=40, unique=True) first_name = models.CharField(max_length=30, blank=True) last_name = models.CharField(max_length=30, blank=True) is_active = models.BooleanField(default=True) is_staff = models.BooleanField(default=False) date_joined = models.DateTimeField(default=timezone.now) objects = UserManager() USERNAME_FIELD = 'email' requireD_FIELDS = ['first_name', 'last_name'] def save(self, *args, **kwargs): super(User, self).save(*args, **kwargs) return self
REQUIRED_FIELDS包含用户模型中的所有必填字段,用户名字段和密码除外,因为这些字段将始终被提示输入。
UserManager 是定义create_user和createsuperuser方法的类。这个类应该在AbstractBaseUser我们上面定义的类之前。让我们继续定义它。
from django.contrib.auth.models import ( AbstractBaseUser, PermissionsMixin, BaseUserManager ) class UserManager(BaseUserManager): def _create_user(self, email, password, **extra_fields): """ Creates and saves a User with the given email,and password. """ if not email: raise ValueError('The given email must be set') try: with transaction.atomic(): user = self.model(email=email, **extra_fields) user.set_password(password) user.save(using=self._db) return user except: raise def create_user(self, email, password=None, **extra_fields): extra_fields.setdefault('is_staff', False) extra_fields.setdefault('is_superuser', False) return self._create_user(email, password, **extra_fields) def create_superuser(self, email, password, **extra_fields): extra_fields.setdefault('is_staff', True) extra_fields.setdefault('is_superuser', True) return self._create_user(email, password=password, **extra_fields)
迁移
迁移提供了一种在每次模型更改时更新数据库模式而不会丢失数据的方法。
为我们的用户模型创建一个初始迁移,并第一次同步数据库。
python manage.py make migrations users python manage.py migrate
创建超级用户
通过运行以下命令创建超级用户:
python manage.py createsuperuser
创建新用户
让我们创建一个端点来启用新用户的注册。我们将从序列化用户模型字段开始。序列化程序提供了一种将数据更改为更易于理解的形式的方法,例如 JSON 或 XML。反序列化恰恰相反,即将数据转换为可以保存到数据库中的形式。
创建 users/serializers.py 并添加以下代码。
# users/serializers.py from rest_framework import serializers from.models import User class UserSerializer(serializers.ModelSerializer): date_joined = serializers.ReadOnlyField() class Meta(object): model = User fields = ('id', 'email', 'first_name', 'last_name', 'date_joined', 'password') extra_kwargs = {'password': {'write_only': True}}
创建用户API视图
接下来,我们要创建一个视图,以便客户端将有一个用于创建新用户的 URL。
在 users.views.py 中,添加以下内容:
# users/views.py class CreateUserAPIView(APIView): # Allow any user (authenticated or not) to access this url permission_classes = (AllowAny,) def post(self, request): user = request.data serializer = UserSerializer(data=user) serializer.is_valid(raise_exception=True) serializer.save() return Response(serializer.data, status=status.HTTP_201_CREATED)
我们设置permission_classes为(AllowAny,)允许任何用户(无论是否经过身份验证)访问此 URL。
配置 URL
创建一个文件users/urls.py 并添加 URL 以匹配我们创建的视图。还要添加以下代码。
# users/urls.py from django.conf.urls import url, patterns from .views import CreateUserAPIView urlpatterns = [ url(r'^create/$', CreateUserAPIView.as_view()), ]
我们还需要将 URL 从用户应用程序导入到主django_auth/urls.py文件。所以继续做吧。我们在include这里使用这个函数,所以不要忘记导入它。
# django_auth/urls.py from django.conf.urls import url, include from django.contrib import admin urlpatterns = [ url(r'^admin/', admin.site.urls), url(r'^user/', include('users.urls', namespace='users')), ]
现在我们已经完成了端点的创建,让我们进行测试,看看我们是否走上正轨。我们将使用 Postman 进行测试。如果您不熟悉 Postman,它是一个提供友好 GUI 的工具,用于构建请求和读取响应。
正如您在上面看到的,端点按预期工作。
验证用户
我们将使用在本教程开头安装的 Django-REST Framework JWT Python 模块。它为 Django Rest Framework JWT 应用程序添加了简单的 JWT 身份验证支持。
但首先,让我们为我们的令牌定义一些配置参数,以及它们是如何在 settings.py 文件中生成的。
# settings.py import datetime JWT_AUTH = { 'JWT_VERIFY': True, 'JWT_VERIFY_EXPIRATION': True, 'JWT_EXPIRATION_DELTA': datetime.timedelta(seconds=3000), 'JWT_AUTH_HEADER_PREFIX': 'Bearer', }
JWT_VERIFY:如果密码错误,它将引发 jwt.DecodeError。
JWT_VERIFY_EXPIRATION: 设置过期时间为True,表示Tokens会在一段时间后过期。默认时间为五分钟。
JWT_AUTH_HEADER_PREFIX: 需要与令牌一起发送的授权头值前缀。我们设置为Bearer,默认为JWT。
在users/views.py中,添加以下代码。
@api_view(['POST']) @permission_classes([AllowAny, ]) def authenticate_user(request): try: email = request.data['email'] password = request.data['password'] user = User.objects.get(email=email, password=password) if user: try: payload = jwt_payload_handler(user) token = jwt.encode(payload, settings.SECRET_KEY) user_details = {} user_details['name'] = "%s %s" % ( user.first_name, user.last_name) user_details['token'] = token user_logged_in.send(sender=user.__class__, request=request, user=user) return Response(user_details, status=status.HTTP_200_OK) except Exception as e: raise e else: res = { 'error': 'can not authenticate with the given credentials or the account has been deactivated'} return Response(res, status=status.HTTP_403_FORBIDDEN) except KeyError: res = {'error': 'please provide a email and a password'} return Response(res)
在上面的代码中,登录视图将用户名和密码作为输入,然后它使用与传递的凭据对应的用户信息创建一个令牌作为负载并将其返回给浏览器。其他用户详细信息(例如姓名)也与令牌一起返回给浏览器。此令牌将用于在以后的请求中进行身份验证。
权限类设置为,allowAny因为任何人都可以访问此端点。
我们还使用此代码存储用户的上次登录时间。
user_logged_in.send(sender=user.__class__, request=request, user=user)
每次用户想要发出 API 请求时,他们都必须在 Auth Headers 中发送令牌以对请求进行身份验证。
让我们用 Postman 测试这个端点。打开 Postman 并使用该请求向您之前创建的其中一个用户进行身份验证。如果登录尝试成功,响应将如下所示:
检索和更新用户
到目前为止,用户可以注册并验证自己。但是,他们还需要一种方法来检索和更新他们的信息。让我们来实现它。
在users.views.py中,添加以下代码。
class UserRetrieveUpdateAPIView(RetrieveUpdateAPIView): # Allow only authenticated users to access this url permission_classes = (IsAuthenticated,) serializer_class = UserSerializer def get(self, request, *args, **kwargs): # serializer to handle turning our `User` object into something that # can be JSONified and sent to the client. serializer = self.serializer_class(request.user) return Response(serializer.data, status=status.HTTP_200_OK) def put(self, request, *args, **kwargs): serializer_data = request.data.get('user', {}) serializer = UserSerializer( request.user, data=serializer_data, partial=True ) serializer.is_valid(raise_exception=True) serializer.save() return Response(serializer.data, status=status.HTTP_200_OK)
我们首先定义权限类并设置为,IsAuthenticated因为这是一个受保护的 URL,只有经过身份验证的用户才能访问它。
然后我们定义一个get 方法来检索用户详细信息。检索用户详细信息后,经过身份验证的用户将根据需要更新他们的详细信息。
更新您的 URL 以定义端点,如下所示:
users/urls.py from .views import CreateUserAPIView, UserRetrieveUpdateAPIView urlpatterns = [ url(r'^update/$', UserRetrieveUpdateAPIView.as_view()), ]
为了使请求成功,标头应包含 JWT 令牌,如下所示。
如果您尝试在没有身份验证标头的情况下请求资源,您将收到以下错误。
如果用户停留超过指定的时间JWT_EXPIRATION_DELTA而没有提出请求,则令牌将过期,他们将不得不请求另一个令牌。这也在下面证明。
结论
本教程涵盖了使用 JSON Web 令牌成功构建可靠的后端身份验证系统所必需的内容。我们还介绍了 Django JWT 身份验证或 Django 授权。
- 要求
- 配置 JWT 设置
- 设置数据库
- 创建模型
- 迁移
- 创建超级用户
- 创建新用户
- 创建用户API视图
- 配置 URL
- 验证用户
- 检索和更新用户
发表评论