Django获取JWT令牌后访问认证页面仍返回401未授权问题排查
问题定位与解决方案
核心问题分析
你遇到的401 Unauthorized错误,本质是两个关键问题:
- 访问
/welcome/时没有在请求头中携带JWT凭证,导致DRF的认证机制无法识别用户身份 - 登录逻辑中的密码验证方式错误,直接对比明文密码与数据库中哈希存储的密码,导致实际登录后的token可能无效(或根本没正确登录)
解决方案分步实施
1. 修复登录视图的密码验证逻辑
Django默认会对用户密码进行哈希存储,直接用user.password == password对比明文和哈希值永远不会匹配。必须使用Django提供的authenticate函数来处理密码验证:
修改views.py中的login函数:
def login(request): if request.method == 'POST': username = request.POST.get('username') password = request.POST.get('password') # 使用Django内置的authenticate函数验证用户,自动处理密码哈希对比 user = authenticate(username=username, password=password) if user is not None: refresh = RefreshToken.for_user(user) token = str(refresh.access_token) response = JsonResponse({'message': 'Login successful'}) response['Authorization'] = f'Bearer {token}' return response else: return JsonResponse({'error_message': 'Invalid username or password.'}, status=400) else: return render(request, 'login.html')
同时确保你的User模型继承自Django的AbstractUser(如果是自定义模型),在models.py中:
from django.contrib.auth.models import AbstractUser class User(AbstractUser): # 自定义字段(如果需要) pass
并在settings.py中添加:
AUTH_USER_MODEL = 'loginapp.User'
2. 修复前端访问/welcome/的凭证携带逻辑
直接用window.location.href = "/welcome/"是普通HTTP GET请求,不会自动带上存在localStorage里的JWT token。有两种可选方案:
方案一:用Fetch请求API并渲染结果
修改login.html中的JavaScript代码,登录成功后携带token请求/welcome/接口,再显示结果:
document.getElementById("loginForm").addEventListener("submit", function(event) { event.preventDefault(); fetch("/", { method: "POST", body: new FormData(document.getElementById("loginForm")) }) .then(response => { if (response.ok) { const token = response.headers.get('Authorization').split(' ')[1]; localStorage.setItem("jwtToken", token); // 携带token请求welcome接口 return fetch("/welcome/", { headers: { 'Authorization': `Bearer ${token}` } }); } else { return response.json(); } }) .then(welcomeRes => { if (welcomeRes.ok) { return welcomeRes.json(); } throw new Error('身份验证失败'); }) .then(welcomeData => { // 替换页面内容为欢迎信息 document.body.innerHTML = `<h1>${welcomeData.message}</h1>`; }) .catch(error => { if (error.error_message) { document.getElementById("error-message").innerText = error.error_message; } else { console.error("错误:", error); } }); });
方案二:改为渲染欢迎页面(更符合预期)
如果希望跳转到一个真正的欢迎页面,而非JSON接口,可修改welcome视图为普通Django视图,并通过Cookie携带token(更安全):
第一步:修改登录视图,将token存入Cookie
def login(request): if request.method == 'POST': username = request.POST.get('username') password = request.POST.get('password') user = authenticate(username=username, password=password) if user is not None: refresh = RefreshToken.for_user(user) token = str(refresh.access_token) response = JsonResponse({'message': 'Login successful'}) # 将token存入HttpOnly Cookie,避免XSS风险 response.set_cookie( 'access_token', token, httponly=True, max_age=3600, # 和token有效期一致(1小时) secure=False, # 生产环境HTTPS改为True samesite='Lax' ) return response else: return JsonResponse({'error_message': 'Invalid username or password.'}, status=400) else: return render(request, 'login.html')
第二步:修改welcome视图为页面渲染视图
from rest_framework_simplejwt.authentication import JWTAuthentication from rest_framework.exceptions import AuthenticationFailed from django.shortcuts import redirect def welcome(request): token = request.COOKIES.get('access_token') if not token: return redirect('login') try: # 验证JWT token jwt_auth = JWTAuthentication() validated_token = jwt_auth.get_validated_token(token) user = jwt_auth.get_user(validated_token) return render(request, 'welcome.html', {'username': user.username}) except AuthenticationFailed: # token无效或过期,重定向到登录页 return redirect('login')
第三步:创建welcome.html模板
<!DOCTYPE html> <html lang="zh-CN"> <head> <meta charset="UTF-8"> <title>欢迎页面</title> </head> <body> <h1>Welcome {{ username }}!</h1> </body> </html>
第四步:修改前端跳转逻辑
登录成功后直接跳转即可,Cookie会自动携带:
// ... 登录成功后 window.location.href = "/welcome/";
额外检查项
- 确保用户是通过
python manage.py createsuperuser或Django的User.objects.create_user()创建的,保证密码是哈希存储的 - 确认
settings.py中的SIMPLE_JWT配置正确,token有效期设置合理
内容的提问来源于stack exchange,提问作者Vrinda Chopra
相关产品推荐
相关产品推荐

