重置PIN时Session未保存邮箱与重置码问题排查
PIN重置功能Session丢失问题排查与修复
问题现状
- 验证重置码(reset_code)通过后,调用SetNewPinView接口时无法从Session中获取之前保存的reset_code和email,返回错误:
{
"error": "Reset code and email not found in session."
}
- 需求:Postman测试SetNewPin接口时仅传入
new_pin和confirm_new_pin两个字段
相关代码
VerifyResetCodeView
class VerifyResetCodeView(APIView): permission_classes = [permissions.AllowAny] def post(self, request): serializer = VerifyResetCodeSerializer(data=request.data) if serializer.is_valid(): reset_code = serializer.validated_data.get('reset_code') try: pin_reset = PinResetCode.objects.get(reset_code=reset_code) except PinResetCode.DoesNotExist: return Response({'error': 'Invalid reset code.'}, status=status.HTTP_400_BAD_REQUEST) if pin_reset.is_expired(): return Response({'error': 'Reset code expired.'}, status=status.HTTP_400_BAD_REQUEST) # Store reset code and email in the session request.session['reset_code'] = reset_code request.session['email'] = pin_reset.email request.session.save() # Explicitly save the session # Debugging statements print(f"Storing - Reset Code: {reset_code}, Email: {pin_reset.email}") print(f"Stored Session Data - Reset Code: {request.session.get('reset_code')}, Email: {request.session.get('email')}") return Response({'message': 'Reset code is Verified. You can now set your new PIN.'}, status=status.HTTP_200_OK) return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
SetNewPinView
class SetNewPinView(APIView): permission_classes = [permissions.AllowAny] def post(self, request): serializer = SetNewPinSerializer(data=request.data) if serializer.is_valid(): new_pin = serializer.validated_data.get('new_pin') confirm_new_pin = serializer.validated_data.get('confirm_new_pin') # Retrieve reset code and email from session reset_code = request.session.get('reset_code') email = request.session.get('email') # Debugging information print(f"Session data - Reset Code: {reset_code}, Email: {email}") # Ensure reset code and email are present if not reset_code or not email: return Response({'error': 'Reset code and email not found in session.'}, status=status.HTTP_400_BAD_REQUEST) # Check if new PINs match if new_pin != confirm_new_pin: return Response({'error': 'New PIN and confirm new PIN do not match.'}, status=status.HTTP_400_BAD_REQUEST) try: pin_reset = PinResetCode.objects.get(email=email, reset_code=reset_code) except PinResetCode.DoesNotExist: return Response({'error': 'Invalid reset code.'}, status=status.HTTP_400_BAD_REQUEST) if pin_reset.is_expired(): return Response({'error': 'Reset code expired.'}, status=status.HTTP_400_BAD_REQUEST) user = get_object_or_404(Employee, email=email) user.set_password(new_pin) user.save() pin_reset.delete() # Clear session data request.session.pop('reset_code', None) request.session.pop('email', None) return Response({'message': 'PIN reset successful.'}, status=status.HTTP_200_OK) return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
测试结果
Postman调用SetNewPinView返回:
{ "error": "Reset code and email not found in session." }
修复方案
1. 解决Postman会话保持问题
Postman默认不会自动维护会话Cookie,需手动处理:
- 调用VerifyResetCode接口后,从响应的
Set-Cookie头中复制sessionid的值 - 在SetNewPin接口的请求头中添加
Cookie: sessionid=xxx(替换xxx为实际值) - 或在Postman设置中开启Automatically send cookies(Settings → General → Automatically send cookies)
2. 检查Django Session配置
确认settings.py中的Session相关配置:
SESSION_ENGINE:确保为django.contrib.sessions.backends.db或其他有效引擎SESSION_COOKIE_SECURE:如果设为True,必须用HTTPS请求,否则Cookie不会被客户端保存SESSION_COOKIE_HTTPONLY:设为True不影响Postman传Cookie,但前端JS无法读取
3. 改用Token替代Session(推荐API场景)
Session更适合Web页面,API场景推荐用一次性Token:
- 在VerifyResetCodeView验证通过后,生成包含
reset_code和email的加密Token(如JWT) - SetNewPinView接收Token并解析,无需依赖Session,示例:
# 安装djangorestframework-simplejwt from rest_framework_simplejwt.tokens import RefreshToken # VerifyResetCodeView返回Token def post(self, request): # ... 原有验证逻辑 ... token = RefreshToken() token['reset_code'] = reset_code token['email'] = pin_reset.email return Response({ 'message': 'Reset code verified', 'access_token': str(token.access_token) }) # SetNewPinView验证Token from rest_framework_simplejwt.authentication import JWTAuthentication class SetNewPinView(APIView): authentication_classes = [JWTAuthentication] permission_classes = [permissions.AllowAny] def post(self, request): reset_code = request.auth.get('reset_code') email = request.auth.get('email') # ... 后续逻辑 ...
4. 确保Serializer仅校验指定字段
修改SetNewPinSerializer,只保留new_pin和confirm_new_pin:
class SetNewPinSerializer(serializers.Serializer): new_pin = serializers.CharField(required=True, min_length=4, max_length=8) confirm_new_pin = serializers.CharField(required=True, min_length=4, max_length=8) def validate(self, attrs): if attrs['new_pin'] != attrs['confirm_new_pin']: raise serializers.ValidationError("New PIN and confirm PIN do not match") return attrs
内容的提问来源于stack exchange,提问作者Sajjad Jagi
相关产品推荐
相关产品推荐

