• 日常搜索
  • 百度一下
  • Google
  • 在线工具
  • 搜转载

Django 中的 JWT 身份验证

本教程将介绍 JSON Web Tokens (JWT) 以及如何在 Django 中实现 JWT 身份验证。

什么是JWT?

JWT 是一个编码的 JSON 字符串,它在标头中传递以对请求进行身份验证。它通常是通过使用密钥对 JSON 数据进行哈希处理而获得的。这意味着服务器不需要每次都查询数据库来检索与给定令牌关联的用户。

JSON Web 令牌如何工作

当用户使用其凭据成功登录时,将获取一个 JSON Web Token 并将其保存在本地存储中。每当用户想要访问受保护的 URL 时,都会在请求的标头中发送令牌。然后服务器检查授权标头中的有效 JWT,如果找到,将允许用户访问。

典型的内容标题如下所示:

Authorization: Bearer eyJhbGciOiJIUzI1NiIsI

下图展示了这个过程:

Django 中的 JWT 身份验证  第1张

认证和授权的概念

认证是识别登录用户的过程,而授权是识别某个用户是否有权访问网络资源的过程。

api范例

在本教程中,我们将在 Django JWT 中构建一个简单的用户身份验证系统作为身份验证机制。

要求

让我们开始吧。

创建一个目录,您将在其中保存您的项目以及一个用于安装项目依赖项的虚拟环境。

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 中的 JWT 身份验证  第2张

正如您在上面看到的,端点按预期工作。

验证用户

我们将使用在本教程开头安装的 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 并使用该请求向您之前创建的其中一个用户进行身份验证。如果登录尝试成功,响应将如下所示:

Django 中的 JWT 身份验证  第3张

检索和更新用户

到目前为止,用户可以注册并验证自己。但是,他们还需要一种方法来检索和更新他们的信息。让我们来实现它。

在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 令牌,如下所示。

Django 中的 JWT 身份验证  第4张

如果您尝试在没有身份验证标头的情况下请求资源,您将收到以下错误。

Django 中的 JWT 身份验证  第5张

如果用户停留超过指定的时间JWT_EXPIRATION_DELTA而没有提出请求,则令牌将过期,他们将不得不请求另一个令牌。这也在下面证明。

Django 中的 JWT 身份验证  第6张

结论

本教程涵盖了使用 JSON Web 令牌成功构建可靠的后端身份验证系统所必需的内容。我们还介绍了 Django JWT 身份验证或 Django 授权。


文章目录
  • 什么是JWT?
  • JSON Web 令牌如何工作
  • 认证和授权的概念
  • api范例
    • 要求
    • 配置 JWT 设置
    • 设置数据库
    • 创建模型
    • 迁移
      • 创建超级用户
    • 创建新用户
    • 创建用户API视图
      • 配置 URL
    • 验证用户
      • 检索和更新用户
  • 结论
  • 发表评论