Django5+Vue3:OA系统前后端分离项目实战-后端登录功能实现(7) date: 2024-08-07 11:00:00
Django5+Vue3 系列文章
前言
此项目采用 Django 框架的 5.0.7 版本进行开发。
Django 5.0 支持的 Python 版本为 3.10、3.11 和 3.12。
CSDN 专栏链接: ~快捷传送门: 留个赞再走呗 😭! ~
登录验证流程:使用 Views 视图函数进行数据校验
打开 apps/oaauth/views.py,定义视图函数
from django.shortcuts import render
from rest_framework.views import APIView
from apps.oaauth.serializers import LoginSerializer, UserSerializer
from datetime import datetime
from .authentications import generate_jwt
from rest_framework import status
from rest_framework.response import Response
# Create your views here.
class LoginView(APIView):
# 登录只能用post方法向服务端发送请求
def post(self,request):
# 1.验证数据是否可用,在此使用序列化校验数据库与客户端发送到数据
serializer = LoginSerializer(data=request.data)
# 如果验证成功
if serializer.is_valid():
# 间接获取数据库数据(attrs['user'] = user)
user = serializer.validated_data.get('user')
# 记录上次登陆的时间
user.last_login = datetime.now()
user.save()
# 登陆成功,生成并返回JWT Token
token = generate_jwt(user)
return Response({'token': token, 'user': UserSerializer(user).data})
# 身份验证失败
else:
print(serializer.errors)
return Response({"message": "参数验证失败!"}, status=status.HTTP_400_BAD_REQUEST)
在定义登录的视图函数时,身份验证是一个关键步骤,通常我们会使用序列化器来处理数据。为此,我们需要创建一个专门的序列化器来进行身份验证。
序列化器: 通过定义数据模型的规则和验证逻辑,确保接收到的数据符合预期格式并进行安全检查。
数据验证自动化:Serializer 序列化器的应用
定义 LoginSerializer 并对 validate 方法进行重写
创建 apps/oaauth/serializers.py 文件,自定义序列化器
from rest_framework import serializers
from .models import OAUser, UserStatusChoices, OADepartment
class LoginSerializer(serializers.Serializer):
# 定义需要验证的字段
email = serializers.EmailField(required=True) # 电子邮箱字段,必填,验证为有效的邮箱格式
password = serializers.CharField(max_length=20, min_length=6) # 密码字段,必填,要求最少6个字符且最多20个字符
# 重写validate方法,以实现自定义校验功能
def validate(self, attrs):
# attrs 包含 email 和 password 数据,从中获取这些数据
email = attrs.get('email')
password = attrs.get('password')
# 验证成功的情况
if email and password:
# 根据 email 查询用户
user = OAUser.objects.filter(email=email).first() # 尝试获取对应邮箱的用户对象
# 检查用户是否存在
if not user:
# 如果用户不存在,抛出验证错误
raise serializers.ValidationError("请输入正确的邮箱!")
# 用户存在,校验密码
if not user.check_password(password): # 使用 Django 自带的方法检查密码
# 如果密码不正确,抛出验证错误
raise serializers.ValidationError("请输入正确的密码!")
# 判断用户状态是否可用
if user.status == UserStatusChoices.UNACTIVE: # 用户状态为未激活
raise serializers.ValidationError("该用户尚未激活")
elif user.status == UserStatusChoices.LOCKED: # 用户状态为锁定
raise serializers.ValidationError("该账号已被锁定,请联系管理员!")
# 视图函数中还会做登录操作,为了节省执行 SQL 语句的次数,将用户对象直接放入 attrs 中,方便在视图中使用
attrs['user'] = user
else:
# 如果没有提供邮箱或密码,抛出验证错误
raise serializers.ValidationError("请传入邮箱和密码!")
# 返回包含验证后数据的字典
return attrs
序列化模型定义与模型嵌套
代码续写如下(示例):
# 继承ModelSerializer模型, 避免重复定义
class DepartmentSerializer(serializers.ModelSerializer):
class Meta:
model = OADepartment
fields = '__all__'
class UserSerializer(serializers.ModelSerializer):
# 重写department
department = DepartmentSerializer()
class Meta:
model = OAUser
# fields = "__all__"
# 排除不需要返回的字段(重要信息以及无需返回的数据)
exclude = ("password", "groups", "user_permissions")
JWT Token 权限认证
创建 apps\oaauth\ authentications.py 文件
使用 JWT Token 进行验证的好处在于它提供了一种自包含、安全且易于在不同服务间传递用户身份信息的方式,同时减少了对服务器的依赖,提高了系统的扩展性和响应速度。
import jwt
import time
from django.conf import settings
from rest_framework.authentication import BaseAuthentication, get_authorization_header
from rest_framework import exceptions
from jwt.exceptions import ExpiredSignatureError
from .models import OAUser
def generate_jwt(user):
"""
生成JWT Token,用于身份验证。
参数:
user (OAUser): 当前登录的用户对象,通常是经过身份验证后的用户
返回:
str: 生成的JWT Token,这个Token包含用户的唯一标识和过期时间
"""
# 设置Token的过期时间为7天后
expire_time = time.time() + 60 * 60 * 24 * 7
# 创建JWT Token,包含用户ID和过期时间,使用SECRET_KEY进行签名
return jwt.encode({'userid': user.pk, 'exp': expire_time}, key=settings.SECRET_KEY, algorithm='HS256')
class JWTAuthentication(BaseAuthentication):
"""
自定义JWT认证类,用于在Django REST Framework中进行JWT认证。
这个类将处理从请求中提取JWT,验证其有效性,并返回对应的用户对象。
"""
keyword = 'JWT' # 认证头部关键字,通常是'JWT'
def authentication(self, request):
"""
从请求对象中提取并验证JWT Token,以进行身份验证。
参数:
request (HttpRequest): HTTP请求对象,可以是来自客户端的HTTP请求
返回:
tuple: (user, token) 用户对象和JWT Token,如果认证失败则返回None
"""
# 从请求头中获取Authorization字段,并按空格分割
auth = get_authorization_header(request).split()
# 检查Authorization头部是否存在,并且是否以'JWT'关键字开头
if not auth or auth[0].lower() != self.keyword.lower().encode():
return None # 认证失败,返回None
# 检查Token的格式是否正确,确保有且只有一个Token
if len(auth) == 1:
msg = '不可用的JWT请求头!' # Token缺失信息
raise exceptions.AuthenticationFailed(msg) # 抛出认证失败异常
elif len(auth) > 2:
msg = '不可用的JWT请求头!JWT Token中间不应该有空格!' # Token包含多余的空格
raise exceptions.AuthenticationFailed(msg) # 抛出认证失败异常
try:
# 提取Token并解码
jwt_token = auth[1]
# 使用SECRET_KEY解码Token
jwt_info = jwt.decode(jwt_token, settings.SECRET_KEY, algorithms=["HS256"])
userid = jwt_info['userid'] # 获取用户ID
try:
# 根据用户ID查找用户,确保用户存在
user = OAUser.objects.get(pk=userid)
setattr(request, 'user', user) # 将用户对象绑定到request上,方便后续使用
return user, jwt_token # 返回用户对象和JWT Token
except OAUser.DoesNotExist:
msg = "JWT Token对应的用户不存在" # 如果找不到用户ID
raise exceptions.AuthenticationFailed(msg) # 抛出认证失败异常
except ExpiredSignatureError:
msg = "JWT Token已过期!" # 处理Token过期情况
raise exceptions.AuthenticationFailed(msg) # 抛出认证失败异常
except jwt.DecodeError:
msg = "无效的JWT Token" # Token解码失败
raise exceptions.AuthenticationFailed(msg) # 抛出认证失败异常
路由映射
创建 apps\oaauth\ urls.py 文件
from django.urls import path
from . import views
# 应用命名空间
app_name = 'oaauth'
urlpatterns = [
# .as_view() 方法会返回一个接受请求参数的可调用对象,这样 Django 就可以正确地将请求分派给类视图的方法
path('login',views.LoginView.as_view(),name='login'),
]
项目根目录添加子路由
打开项目根目录的 oaback\urls.py 文件,添加子路由
from django.contrib import admin
from django.urls import path, include
urlpatterns = [
path("admin/", admin.site.urls),
path("auth/",include('apps.oaauth.urls'))
]
Postman 测试
验证:失败情况示例
验证:成功情况示例
总结
本节内容成功实现了后端的登录和身份验证功能,并通过 Postman 工具进行了全面的测试验证。
- 感谢你赐予我前进的力量
赞赏者名单
因为你们的支持让我意识到写文章的价值🙏
本文是原创文章,采用 CC BY-NC-ND 4.0 协议,完整转载请注明来自 Fender