You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Django获取JWT令牌后访问认证页面仍返回401未授权问题排查

问题定位与解决方案

核心问题分析

你遇到的401 Unauthorized错误,本质是两个关键问题:

  1. 访问/welcome/时没有在请求头中携带JWT凭证,导致DRF的认证机制无法识别用户身份
  2. 登录逻辑中的密码验证方式错误,直接对比明文密码与数据库中哈希存储的密码,导致实际登录后的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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.25 18:14:55