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

登录后刷新页面出现CSRF验证失败错误的问题求助

问题描述

成功登录后刷新页面,系统提示重新提交表单,点击继续后出现“CSRF verification failed. Request aborted.”错误。

已尝试方案

使用@csrf_exempt装饰器可解决问题,但因涉及登录表单,存在安全风险,故不考虑该方案。

相关代码

login.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Login</title>
</head>
<body>
    Home

    {% if user.is_authenticated %}
        Hello {{name}} you are logged<br>
        <a href="{% url 'logout' %}">logout</a>
    {% else %}
    <form method="post">
    {% csrf_token %}
    {{form.as_p}}
    <button type="submit">login</button>
    {% endif %}
</form>
</body>
</html>

views.py

from django.shortcuts import render, redirect
from login_app.forms import LoginForm, UserRegistrationForm
from django.contrib.auth import authenticate, login, logout
from django.middleware.csrf import get_token
from django.http import HttpResponseForbidden

# Create your views here.


def home(request):
    form = LoginForm()
    return render(request,'home.html',{'form':form})


def log_in(request):
    if request.method == 'POST':
        form = LoginForm(request.POST)
        # if request.POST.get('csrfmiddlewaretoken') == get_token(request):
            # return HttpResponseForbidden('invalid')
        if form.is_valid():
            email = form.cleaned_data['email']
            password =  form.cleaned_data['password']
            try:
                user = authenticate(email = email,password=password)
                if user is None:
                    raise ValueError('not found')
                print(login(request,user))
                login(request,user)
                return render(request,'after.html',{'name':user})
                # return redirect('home')
            except ValueError as e:
                return redirect('home')
    else:
        form = LoginForm()
    return redirect('home')


def log_out(request):
    logout(request)
    return redirect('home')

models.py

from typing import Any
from django.db import models
from django.contrib.auth.models import AbstractBaseUser, PermissionsMixin, UserManager
from django.contrib.auth import authenticate

# Create your models here.

class CustomUserManager(UserManager):
    def _create_user(self, email, password, **extra_fields):
        if not email:
            raise ValueError("Email can not be empty")
        
        email = self.normalize_email(email)
        user = self.model(email = email, **extra_fields)
        user.set_password(password)
        user.save(using=self._db)
        return user
    
    def create_user(self, email=None, password=None, **extra_fields):
        extra_fields.setdefault('is_staff', False)
        extra_fields.setdefault('is_superuser', False)
        return self._create_user(email, password, **extra_fields)
    
    def create_superuser(self, email=None, password=None, **extra_fields):
        extra_fields.setdefault('is_staff',True)
        extra_fields.setdefault('is_superuser', True)
        return self._create_user(email,password, **extra_fields)
    
class User(AbstractBaseUser, PermissionsMixin):
    email = models.EmailField(blank=True, default='',unique=True)
    phone_number = models.CharField(max_length=12,unique=True,blank=True,default='')
    password = models.CharField(max_length=200)
    name= models.CharField(max_length=255,blank=True,default='')

    is_active = models.BooleanField(default=True)
    is_superuser = models.BooleanField(default=False)
    is_staff = models.BooleanField(default=False)

    date_joined = models.DateTimeField(auto_now_add=True)
    last_login = models.DateTimeField(blank=True, null=True)

    objects = CustomUserManager()

    USERNAME_FIELD = 'email'
    EMAIL_FIELD = 'email'
    REQUIRED_FIELDS = []

    class Meta:
        verbose_name = 'Custom User'
        verbose_name_plural = 'Custom Users'

    def get_full_name(self):
        return self.name
    
    def get_short_name(self):
        return self.name or self.email.split('@')[0]

urls.py

from django.urls import path
from login_app.views import home, log_in, log_out

urlpatterns = [
    path("",home,name='home'),
    path("log_in",log_in,name='log_in'),
    path("log_out",log_out,name='log_out'),
]

forms.py

from django import forms
from login_app.models import User

class LoginForm(forms.Form):
    email = forms.EmailField(label='Email',max_length=100)
    password = forms.CharField(label='Password',widget=forms.PasswordInput)


class UserRegistrationForm(forms.ModelForm):
    password = forms.CharField(widget=forms.PasswordInput)

    class Meta:
        model = User
        fields = ['phone_number','email','name','password']
解决方案

问题核心是登录成功后直接用render返回页面,浏览器会保留POST请求上下文,刷新时会重新提交已失效的POST请求,导致CSRF验证失败。需遵循**Post/Redirect/Get (PRG)**模式,登录成功后重定向到GET请求页面。

1. 修改views.py中的log_in函数

def log_in(request):
    if request.method == 'POST':
        form = LoginForm(request.POST)
        if form.is_valid():
            email = form.cleaned_data['email']
            password = form.cleaned_data['password']
            user = authenticate(email=email, password=password)
            if user is not None:
                login(request, user)
                # 替换原render为redirect,跳转到登录后的页面
                return redirect('home')
            # 验证失败返回登录页并提示错误
            return render(request, 'home.html', {'form': form, 'error': 'Invalid email or password'})
    else:
        form = LoginForm()
    return render(request, 'home.html', {'form': form})

2. 调整模板展示错误提示(以home.html为例)

在模板中添加错误提示代码,方便用户看到登录失败原因:

{% if error %}
    <p style="color:red;">{{ error }}</p>
{% endif %}

3. 额外检查项

  • 确保settings.py中启用django.middleware.csrf.CsrfViewMiddleware中间件(默认已启用)
  • 确认登录表单中{% csrf_token %}标签位置正确(代码中已正确添加)
  • 验证自定义用户模型的authenticate方法逻辑正常,确保用户登录后会话正确建立

内容的提问来源于stack exchange,提问作者Smith

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.23 00:27:26