使用 Django Rest API 实现 JWT 身份验证和用户配置文件第 3 部分
介绍
欢迎回到 React 和 Django 系列的第三部分,我们将从零开始创建一个 Notes 应用。众所周知,安全性在任何 Web 应用中都至关重要,而身份验证是其中至关重要的一个方面。这正是 JWT 的用武之地。JSON Web Tokens (JWT) 是一种广泛使用的标准,用于在各方之间以 JSON 对象的形式安全地传输信息。JWT 允许我们通过加密并以令牌的形式传输用户数据来验证用户身份,并保护我们的应用安全。它是一个很棒的身份验证选项,因为它允许我们将用户信息直接存储在令牌中,从而方便在后续的每个请求中验证用户身份。在本文中,我们将实现 JWT 身份验证,以确保用户数据的安全。那么,让我们深入学习如何使用 JWT 身份验证来保护我们的 Notes 应用。
安装必要的软件包
在我们开始之前,我们需要安装 JWT 所需的必要 django 包,它们是:
pip install djangorestframework-simplejwt
djangorestframework-simplejwt 包提供了一种在 Django REST 框架应用中实现 JWT 身份验证的简单方法。它包含用于生成和刷新 JWT 令牌的视图和序列化器,以及用于验证令牌的内置令牌身份验证后端。
接下来,我们可以将其包含在已安装的应用程序中并将其添加到设置中:
INSTALLED_APPS = [
'rest_framework_simplejwt.token_blacklist',
]
令牌黑名单功能会维护一个已撤销或过期令牌列表,并根据该列表检查每个新收到的令牌,以确保其仍然有效。这有助于防止诸如令牌盗窃或重放攻击之类的安全漏洞。
接下来,我们设置 JWT:
SIMPLE_JWT = {
'ACCESS_TOKEN_LIFETIME': timedelta(minutes=180),
'REFRESH_TOKEN_LIFETIME': timedelta(days=50),
'ROTATE_REFRESH_TOKENS': True,
'BLACKLIST_AFTER_ROTATION': True,
'UPDATE_LAST_LOGIN': False,
'ALGORITHM': 'HS256',
'VERIFYING_KEY': None,
'AUDIENCE': None,
'ISSUER': None,
'JWK_URL': None,
'LEEWAY': 0,
'AUTH_HEADER_TYPES': ('Bearer',),
'AUTH_HEADER_NAME': 'HTTP_AUTHORIZATION',
'USER_ID_FIELD': 'id',
'USER_ID_CLAIM': 'user_id',
'USER_AUTHENTICATION_RULE': 'rest_framework_simplejwt.authentication.default_user_authentication_rule',
'AUTH_TOKEN_CLASSES': ('rest_framework_simplejwt.tokens.AccessToken',),
'TOKEN_TYPE_CLAIM': 'token_type',
'TOKEN_USER_CLASS': 'rest_framework_simplejwt.models.TokenUser',
'JTI_CLAIM': 'jti',
'SLIDING_TOKEN_REFRESH_EXP_CLAIM': 'refresh_exp',
'SLIDING_TOKEN_LIFETIME': timedelta(minutes=5),
'SLIDING_TOKEN_REFRESH_LIFETIME': timedelta(days=1),
}
以下是一些关键设置的解释:
ACCESS_TOKEN_LIFETIME:设置访问令牌的有效期,访问令牌用于授予对受保护资源的访问权限。在本例中,它设置为 180 分钟(或 3 小时)。
REFRESH_TOKEN_LIFETIME:设置刷新令牌的有效期,用于在原始令牌过期后获取新的访问令牌。在本例中,设置为 50 天。
ROTATE_REFRESH_TOKENS:此设置控制在颁发新的访问令牌时是否轮换刷新令牌。如果设置为 True,则每次颁发新的访问令牌时都会颁发一个新的刷新令牌。
BLACKLIST_AFTER_ROTATION:此设置控制已轮换的刷新令牌是否被列入黑名单。如果设置为 True,则在发出新的刷新令牌时,所有先前的刷新令牌都将失效。
算法:设置用于签署 JWT 令牌的算法。在本例中,它设置为 HS256,这是一种使用共享密钥来签署和验证令牌的对称密钥算法。
AUTH_HEADER_TYPES:指定可用于发送 JWT 令牌的身份验证标头的类型。在本例中,它被设置为 ('Bearer',),这是最常用的类型。
AUTH_HEADER_NAME:设置用于发送身份验证令牌的 HTTP 标头的名称。在本例中,设置为“HTTP_AUTHORIZATION”。
USER_ID_FIELD:设置用于在 JWT 令牌中识别用户的字段名称。在本例中,设置为“id”。
USER_ID_CLAIM:设置用于在 JWT 令牌中存储用户 ID 的声明名称。在本例中,它被设置为“user_id”。
AUTH_TOKEN_CLASSES:设置身份验证系统将接受的身份验证令牌的类别。在本例中,它被设置为 ('rest_framework_simplejwt.tokens.AccessToken',),这是默认的令牌类。
JTI_CLAIM:设置用于存储 JWT 令牌唯一标识符的声明名称。在本例中,设置为“jti”。
配置 URL
现在我们已经配置了设置,我们现在可以转到配置我们的身份验证 URL,我们将在应用程序级别创建访问令牌和刷新令牌的 URL,因为我们只对一个应用程序进行身份验证:
urlpatterns = [
#Authentication
path('token/', views.MyTokenObtainPairView.as_view(), name='token_obtain_pair'),
path('token/refresh/', TokenRefreshView.as_view(), name='token_refresh'),
]
太好了,现在我们已经完成了设置,我们可以为我们的 Notes 应用程序用户创建一个配置文件,以便只有经过身份验证的用户才能访问该应用程序。我们可以首先这样做:
- 创建用户模型
from django.contrib.auth.models import AbstractUser
class CustomUser(AbstractUser):
bio = models.CharField(max_length=255, blank=True)
cover_photo = models.ImageField(upload_to='covers/', null=True, blank=True)
通过创建扩展 AbstractUser 的自定义用户模型,您可以添加特定于您的应用程序的字段,并提供默认用户模型之外的附加功能。
2.更新我们原来的 Note 模型
user = models.ForeignKey(CustomUser, on_delete=models.CASCADE, null=True, blank=True, related_name='notes')
这行代码定义了 Note 模型和 CustomUser 模型之间的外键关系。ForeignKey 字段在 Note 和 CustomUser 之间建立了多对一的关系,这意味着每个 Note 只与一个 CustomUser 实例关联,但一个 CustomUser 可以拥有多个 Note 实例。
3.**创建用户资料、注册和登录的视图
#Login User
class MyTokenObtainPairView(TokenObtainPairView):
serializer_class = MyTokenObtainPairSerializer
#Register User
class RegisterView(generics.CreateAPIView):
queryset = CustomUser.objects.all()
permission_classes = (AllowAny,)
serializer_class = RegisterSerializer
#api/profile and api/profile/update
@api_view(['GET'])
@permission_classes([IsAuthenticated])
def getProfile(request):
user = request.user
serializer = ProfileSerializer(user, many=False)
return Response(serializer.data)
@api_view(['PUT'])
@permission_classes([IsAuthenticated])
def updateProfile(request):
user = request.user
serializer = ProfileSerializer(user, data=request.data, partial=True)
if serializer.is_valid():
serializer.save()
return Response(serializer.data)
#api/notes
@api_view(['GET'])
@permission_classes([IsAuthenticated])
def getNotes(request):
public_notes = Note.objects.filter(is_public=True).order_by('-updated')[:10]
user_notes = request.user.notes.all().order_by('-updated')[:10]
notes = public_notes | user_notes
serializer = NoteSerializer(notes, many=True)
return Response(serializer.data)
第一个代码定义了一个自定义令牌获取视图,该视图使用自定义序列化器为用户获取 JSON Web 令牌 (JWT) 对。
第二段代码定义了一个用于注册新用户的视图,使用了 Django 内置的 CreateAPIView 接口,并将权限类型设置为 AllowAny,即任何人都可以访问该视图。
接下来的两段代码定义了用于获取和更新用户个人资料的视图。getProfile 视图返回当前已验证用户个人资料的序列化数据,而 updateProfile 视图则使用请求中的数据更新个人资料。这两个视图都要求用户使用 IsAuthenticated 权限类进行身份验证。
最后,最后一段代码定义了一个用于获取用户笔记的视图。它首先过滤公开笔记,然后过滤用户笔记,然后将它们合并并以序列化形式返回。第一段代码定义了一个自定义令牌获取视图,该视图使用自定义序列化器获取用户的 JSON Web Token (JWT) 对。
第二段代码定义了一个用于注册新用户的视图。它使用内置的 Django CreateAPIView 并将权限类设置为 AllowAny,这意味着任何人都可以访问此视图。
接下来的两段代码定义了用于获取和更新用户个人资料的视图。getProfile 视图返回当前已验证用户个人资料的序列化数据,而 updateProfile 视图则使用请求中的数据更新个人资料。这两个视图都要求用户使用 IsAuthenticated 权限类进行身份验证。
最后,最后一段代码定义了一个用于获取用户笔记的视图。它首先过滤公开笔记,然后过滤用户笔记,然后将它们合并并以序列化形式返回。此视图还要求用户使用 IsAuthenticated 权限类进行身份验证。此视图还要求用户使用 IsAuthenticated 权限类进行身份验证。
- 添加我们的序列化器
class MyTokenObtainPairSerializer(TokenObtainPairSerializer):
@classmethod
def get_token(cls, user):
token = super().get_token(user)
# Add custom claims
token['username'] = user.username
token['email'] = user.email
# ...
return token
class RegisterSerializer(serializers.ModelSerializer):
password = serializers.CharField(
write_only=True, required=True, validators=[validate_password])
password2 = serializers.CharField(write_only=True, required=True)
email = serializers.EmailField(
required=True,
validators=[UniqueValidator(queryset=CustomUser.objects.all())]
)
class Meta:
model = CustomUser
fields = ('username', 'email', 'password', 'password2', 'bio', 'cover_photo')
def validate(self, attrs):
if attrs['password'] != attrs['password2']:
raise serializers.ValidationError(
{"password": "Password fields didn't match."})
return attrs
def create(self, validated_data):
user = CustomUser.objects.create(
username=validated_data['username'],
email=validated_data['email'],
bio=validated_data['bio'],
cover_photo=validated_data['cover_photo']
)
user.set_password(validated_data['password'])
user.save()
return user
class ProfileSerializer(serializers.ModelSerializer):
notes = NoteSerializer(many=True, read_only=True)
class Meta:
model = CustomUser
fields = '__all__'
MyTokenObtainPairSerializer:TokenObtainPairSerializer 的子类,用于将自定义声明(用户名、邮箱等)添加到令牌有效负载中。RegisterSerializer
:用于用户注册流程的序列化器。它会验证密码字段,检查邮箱地址是否重复,并在所有字段均有效的情况下创建一个新的 CustomUser 实例。ProfileSerializer
:用于用户个人资料数据的序列化器。它包含 CustomUser 模型中的所有字段,并使用 NoteSerializer 序列化相关的注释。
- ** 配置 URL **
#Authentication
path('token/', views.MyTokenObtainPairView.as_view(), name='token_obtain_pair'),
path('token/refresh/', TokenRefreshView.as_view(), name='token_refresh'),
path('register/', views.RegisterView.as_view(), name='auth_register'),
#Profile
path('profile/', views.getProfile, name='profile'),
path('profile/update/', views.updateProfile, name='update-profile'),
注意:我没有包含注释网址,因为我在本系列的第一部分中已经解释过了
好的,完成这些步骤后,我们现在可以测试我们的后端是否按预期工作,但首先我们需要应用迁移,因为我们已经创建并修改了我们的表:
python manage.py makemigrations
python manage.py migrate
使用 Postman 测试我们的后端
现在让我们使用 Postman 测试我们的后端。Postman 是一款流行的软件应用程序,允许开发人员测试和调试 API。使用 Postman,开发人员可以向 Web 服务器发送 HTTP 请求并查看响应。
- 注册我们的用户
我们向 'api/register/' 发出 POST 请求来创建新用户
2.登录
为了登录我们的新用户,我们将使用 POST“api/token”端点来生成我们的访问和刷新令牌以允许身份验证,我们可以手动复制它们,因为我们正在使用 Postman 进行测试,并且尚未使用我们的 React 前端配置端点
3.检查我们的用户配置文件
为了使用邮递员处理经过身份验证的请求,我们必须将登录时获得的访问令牌粘贴到授权中的承载令牌中,例如 GET‘api/profile’:
从而得到我们创建的用户配置文件:
4.获取您的笔记
用户现在可以通过 GET‘api/notes’端点进行身份验证来访问他们的笔记:
请注意,列表为空,因为用户尚未创建笔记
结论
最后,我要衷心感谢您抽出时间阅读这篇关于 React Django 笔记应用中 Django 后端 JWT 身份验证的文章。希望本文能够帮助我们构建一个完全身份验证的应用。在本系列的下一篇中,我们将深入探讨如何将身份验证与 React 前端集成,以便最终展现应用的完整身份验证功能。如果您有兴趣探索代码,可以在 Github 上找到该应用的链接 ( ReactDjango Notes App )。再次感谢您的阅读,期待很快与您分享本系列的下一篇。
文章来源:https://dev.to/ki3ani/implementing-jwt-authentication-and-user-profile-with-django-rest-api-part-3-3dh9