Django Rest会话存储OTP前端验证失败,Postman测试正常求助
问题:Session存储OTP跨请求丢失(Postman正常,前端调用失败)
项目环境
- 后端:Django Rest Framework
- 前端:WordPress(自定义HTML/JS实现OTP验证流程)
- 核心逻辑:OTP不存储到数据库,临时存在Session中,用户提交OTP时从Session读取验证
问题现象
- Postman测试
/send_otp/和/login/接口,Session中的OTP可正常读取,登录验证功能正常 - 前端HTML/JS调用时,
/send_otp/接口成功生成并存储OTP,但/login/接口读取request.session.get("otp_code")返回None,导致OTP验证失败
相关代码
后端视图代码
SendOTPView
class SendOTPView(APIView): def post(self, request): serializer = OTPSerializer(data=request.data) if serializer.is_valid(): phone_number = serializer.validated_data["phone"] otp_code = randint(100000, 999999) request.session['otp_code'] = otp_code print("otp in sendOTP",request.session.get("otp_code")) otp_send(phone_number, otp_code) return Response( {"detail": "OTP sent successfully"}, status=status.HTTP_200_OK ) return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
UserLoginView
class UserLoginView(APIView): def post(self, request): serializer = UserLoginSerializer(data=request.data) if serializer.is_valid(): stored_otp = request.session.get("otp_code") print(stored_otp) user_entered_otp = serializer.validated_data["otp"] phone_number = serializer.validated_data["phone"] try: user_from_db = User.objects.get(username=phone_number) except: return Response({"detail": "user not found"}, status=status.HTTP_404_NOT_FOUND) password = generate_password(phone_number) if str(user_entered_otp) == str(stored_otp): del request.session['otp_code'] user = authenticate(username=phone_number, password=password) if user: return Response({"detail": 'logged in successfully '}, status=status.HTTP_200_OK) else: return Response( {"detail": "Invalid phone or otp"}, status=status.HTTP_400_BAD_REQUEST, ) else : return Response( { "detail": "Wrong otp code", }, status=status.HTTP_400_BAD_REQUEST,) return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
前端HTML代码
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <meta name="csrf-token" content="{{ csrf_token }}"> <title>OTP Verification</title> </head> <body> <div id="phone-form"> <h2>Send OTP</h2> <input type="text" id="phone" placeholder="Enter your phone number"> <button onclick="sendOTP()">Send OTP</button> </div> <div id="otp-form" style="display: none;"> <h2>Verify OTP</h2> <input type="hidden" id="otp-phone"> <input type="text" id="otp" placeholder="Enter OTP"> <button onclick="verifyOTP()">Verify OTP</button> </div> <script> const baseURL = 'http://127.0.0.1:8000/auth'; function getCSRFToken() { return document.querySelector('meta[name="csrf-token"]').getAttribute('content'); } function sendOTP() { const phone = document.getElementById('phone').value; fetch(`${baseURL}/send_otp/`, { method: 'POST', headers: { 'Content-Type': 'application/json', 'X-CSRFToken': getCSRFToken() }, credentials: 'include', body: JSON.stringify({ phone }) }) .then(response => { if (response.ok) { alert('OTP sent successfully'); document.getElementById('phone-form').style.display = 'none'; document.getElementById('otp-form').style.display = 'block'; document.getElementById('otp-phone').value = phone; } else { alert('Failed to send OTP'); } }) .catch(error => console.error('Error:', error)); } function verifyOTP() { const phone = document.getElementById('otp-phone').value; const otp = document.getElementById('otp').value; fetch(`${baseURL}/login/`, { method: 'POST', headers: { 'Content-Type': 'application/json', 'X-CSRFToken': getCSRFToken() }, credentials: 'include', body: JSON.stringify({ phone, otp }) }) .then(response => { if (response.ok) { alert('Login successful'); window.location.href = '/'; } else { alert('Failed to verify OTP'); } }) .catch(error => console.error('Error:', error)); } </script> </body> </html>
前端JS代码
const baseURL = 'http://127.0.0.1:8000/auth'; function getCSRFToken() { const cookieValue = document.cookie .split('; ') .find(row => row.startsWith('csrftoken=')) .split('=')[1]; return cookieValue; } function sendOTP() { const phone = document.getElementById('phone').value; fetch(`http://127.0.0.1:8000/auth/send_otp/`, { method: 'POST', headers: { 'Content-Type': 'application/json', 'X-CSRFToken': getCSRFToken() }, credentials: 'include', body: JSON.stringify({ phone }) }) .then(response => { if (response.ok) { alert('OTP sent successfully'); document.getElementById('phone-form').style.display = 'none'; document.getElementById('otp-form').style.display = 'block'; document.getElementById('otp-phone').value = phone; } else { alert('Failed to send OTP'); } }) .catch(error => console.error('Error:', error)); } function verifyOTP() { const phone = document.getElementById('otp-phone').value; const otp = document.getElementById('otp').value; fetch(`http://127.0.0.1:8000/auth/login/`, { method: 'POST', headers: { 'Content-Type': 'application/json', 'X-CSRFToken': getCSRFToken() }, credentials: 'include', body: JSON.stringify({ phone, otp }) }) .then(response => { if (response.ok) { alert('Login successful'); window.location.href = '/'; } else { alert('Failed to verify OTP'); } }) .catch(error => console.error('Error:', error)); }
排查方向与解决方法
1. 跨域Cookie/Session共享问题
这是最可能的原因——前端WordPress和后端Django端口/域名不同,属于跨域场景,浏览器默认会限制Cookie的跨域携带:
- 修改Django配置文件
settings.py:# 允许跨域携带凭证 CORS_ALLOW_CREDENTIALS = True # 添加前端域名到允许列表(比如WordPress运行在http://localhost:80) CORS_ALLOWED_ORIGINS = [ "http://localhost:80", "http://127.0.0.1:80", ] # 设置Session Cookie跨域可用 SESSION_COOKIE_SAMESITE = 'None' # 生产环境必须设为True(HTTPS),本地测试可暂时设为False SESSION_COOKIE_SECURE = False # 同步设置CSRF Cookie CSRF_COOKIE_SAMESITE = 'None' CSRF_COOKIE_SECURE = False - 确保前端fetch请求的
credentials参数为'include'(你的代码已设置,无需修改)
2. CSRF Token一致性问题
- 前端HTML中的
{{ csrf_token }}是WordPress的模板变量,无法生成Django的CSRF Token,必须使用从Cookie中读取Token的JS版本(即你提供的单独JS代码中的getCSRFToken方法) - 替换HTML中的script部分为正确的JS代码,避免使用meta标签中的错误CSRF Token
3. Session存储验证
- 检查Django的Session配置,确保
SESSION_ENGINE使用的是可用的存储后端(默认django.contrib.sessions.backends.db需执行python manage.py migrate创建Session表) - 在两个视图中打印
request.session.session_key,确认两次请求的session_key是否一致:如果不一致,说明请求没有共享同一个Session,需检查Cookie是否正确携带
4. 浏览器调试
- 打开浏览器开发者工具的Network面板,查看
/send_otp/和/login/请求的Request Headers,确认Cookie字段中包含sessionid和csrftoken - 查看Response Headers中的
Set-Cookie字段,确认Session Cookie的属性(SameSite、Secure)符合配置
内容的提问来源于stack exchange,提问作者Novartis Nk
相关产品推荐
相关产品推荐

