CSRF 令牌丢失或不正确 - 在 Django 中使用自动完成灯
Posted
技术标签:
【中文标题】CSRF 令牌丢失或不正确 - 在 Django 中使用自动完成灯【英文标题】:CSRF token missing or incorrect - using auto-complete light in Django 【发布时间】:2022-01-13 18:13:09 【问题描述】:在我的 Django 应用程序中遇到 CSRF_Token 丢失或不正确,我真的不明白问题出在哪里,因为我已将 % csrf_token % 包含在我的模板呈现的任何可能的形式中。我怀疑这可能与在表单中完成以检索区域名称等的 ajax 请求有关,也许有人可以告诉我问题是什么。我正在使用 autocomplete-light 从我的数据库中检索一些数据,不知道这是否可以发挥作用。我尝试在网上四处搜索,但没有找到似乎适用于我的问题的解决方案。
Views.py
Class BreedAutocomplete(autocomplete.Select2QuerySetView):
def get_queryset(self):
if not self.request.user.is_authenticated:
return DogBreeds.objects.none()
qs = DogBreeds.objects.all()
if self.q:
qs = qs.filter(name__istartswith=self.q)
return qs
class AdListTakeMyDog(generic.ListView):
model = Advertisement
context_object_name = 'ads'
template_name = 'core/advertisement_list_take.html'
def get_queryset(self):
queryset = Advertisement.objects.filter(is_offering_own_dog=True)
return queryset
class AdListGetMeADog(generic.ListView):
model = Advertisement
context_object_name = 'ads'
template_name = 'core/advertisement_list_get.html'
def get_queryset(self):
queryset = Advertisement.objects.filter(is_offering_own_dog=False)
return queryset
class NewAdTakeMyDog(CreateView):
model = Advertisement
form_class = NewAdTakeMyDogForm
success_url = reverse_lazy('view_ads_take_my_dog')
template_name = 'core/advertisement_form_take.html'
def form_valid(self, form):
form.instance.author = self.request.user
form.instance.is_offering_own_dog = True
return super().form_valid(form)
class NewAdGetMeADog(CreateView):
model = Advertisement
form_class = NewAdGetMeADogForm
success_url = reverse_lazy('ad_changelist')
template_name = 'core/advertisement_form_get.html'
def form_valid(self, form):
form.instance.author = self.request.user
form.instance.is_offering_own_dog = False
return super().form_valid(form)
advertisement_create - 模板
% extends '_base.html' %
% load static %
% block content %
<body onload="hide_area_field()">
% block form_title %
<h2>Ny annons</h2>
% endblock form_title %
% if form.is_multipart %
<form method="post" enctype="multipart/form-data" id="adForm" data-municipalities-url="% url 'ajax_load_municipalities' %" data-areas-url="% url 'ajax_load_areas' %" novalidate>
% csrf_token %
% else %
<form method="post" id="adForm" data-municipalities-url="% url 'ajax_load_municipalities' %" data-areas-url="% url 'ajax_load_areas' %" novalidate>
% csrf_token %
% endif %
<table>
form.as_p
</table>
<button type="submit">Publicera annons</button>
form.media
</form>
</body>
% endblock content %
% block footer %
% comment % Imports for managing Django Autocomplete Light in form % endcomment %
<script type="text/javascript" src="% static 'admin/js/vendor/jquery/jquery.js' %"></script>
<link rel="stylesheet" type="text/css" href="% static 'autocomplete_light/select2.css' %" />
<script type="text/javascript" src="% static 'autocomplete_light/jquery.init.js' %"></script>
<script type="text/javascript" src="% static 'autocomplete_light/autocomplete.init.js' %"></script>
<script type="text/javascript" src="% static 'autocomplete_light/select2.js' %"></script>
<link rel="stylesheet" type="text/css" href="% static 'autocomplete_light/vendor/select2/dist/css/select2.css' %" />
<script type="text/javascript" src="% static 'autocomplete_light/vendor/select2/dist/js/select2.full.js' %"></script>
<script type="text/javascript" src="% static 'autocomplete_light/forward.js' %"></script>
<script type="text/javascript" src="% static 'admin/js/vendor/jquery/jquery.js' %"></script>
% comment % Functions for handling area selectors % endcomment %
<script>
function hide_area_field()
const area_selector = document.getElementById('id_area');
area_selector.style.display = 'none';
/* Get URL for requesting list of municipalities & Areas */
const baseURI = window.location.origin
const municipality_url = document.getElementById('adForm')['attributes']['data-municipalities-url']['value']
const area_url = document.getElementById('adForm')['attributes']['data-areas-url']['value']
/* Get relevant objects from DOM */
const province_selector = document.getElementById('id_province');
const municipality_selector = document.getElementById('id_municipality');
const area_selector = document.getElementById('id_area');
/* Update municipalities when Province have been chosen */
province_selector.addEventListener('change', (event) =>
var xhr = new XMLHttpRequest();
xhr.onload = function ()
if (xhr.readyState === xhr.DONE)
if (xhr.status === 200)
municipality_selector.innerHTML = xhr.response
;
xhr.open('GET', baseURI + municipality_url, true);
xhr.setRequestHeader('province', province_selector.value)
xhr.send();
);
/* Update Areas when Municipiality have been chosen */
municipality_selector.addEventListener('change', (event) =>
var xhr = new XMLHttpRequest();
xhr.onload = function ()
if (xhr.readyState === xhr.DONE)
if (xhr.status === 200)
if (xhr.response.length > 58)
area_selector.style.display = ''
area_selector.innerHTML = xhr.response
else
area_selector.style.display = 'none'
;
xhr.open('GET', baseURI + area_url, true);
xhr.setRequestHeader('municipality', municipality_selector.value)
xhr.send();
);
</script>
% endblock footer %
forms.py
class NewAdTakeMyDogForm(forms.ModelForm):
breed = forms.ModelChoiceField(
queryset=DogBreeds.objects.all(),
widget=autocomplete.ModelSelect2(url='breed-autocomplete')
)
class Meta:
model = Advertisement
fields = ('province', 'municipality', 'area', 'title', 'description', 'days_per_week', 'size_offered', 'breed', 'image1', 'image2', 'image3')
def __init__(self, *args, **kwargs):
super(NewAdTakeMyDogForm, self).__init__(*args, **kwargs)
self.fields['municipality'].queryset = Municipality.objects.none()
self.fields['area'].queryset = Area.objects.none()
self.fields['area'].required = False
if 'province' in self.data:
try:
# Set municipality queryset
province_id = int(self.data.get('province'))
self.fields['municipality'].queryset = Municipality.objects.filter(province_id=province_id).order_by('name')
# Set area queryset
municipality_id = int(self.data.get('municipality'))
self.fields['area'].queryset = Area.objects.filter(municipality_id=municipality_id).order_by('name')
except (ValueError, TypeError):
pass # invalid input from the client; ignore and fallback to empty Municipality/Area queryset
【问题讨论】:
【参考方案1】:错误是我查看了错误的模板,我有多个继承模板,并且把它们弄混了,所以我虽然在表单中呈现了一个 CSRF 令牌,但它是在一个模板中完成的没有被使用。经验教训 - 拥有一个您从其开始的 BASE 模板,并且仅将块用于在基本模板之间更改的基本代码。
【讨论】:
【参考方案2】:您的 AJAX 请求不包含 CSRF 令牌。于是 Django 的跨站请求伪造保护开始起作用了。
姜戈documentation 详细解释了如何在发送 AJAX POST 请求时获取 CSRF 令牌以及如何将其包含在请求的标头中。
【讨论】:
我的两个AJAX请求都是GET请求,应该没问题,看不出是哪一个触发了CSRF以上是关于CSRF 令牌丢失或不正确 - 在 Django 中使用自动完成灯的主要内容,如果未能解决你的问题,请参考以下文章
Django:CSRF 令牌丢失或不正确。 / 避免 % csrf_token %
CSRF 令牌丢失或不正确。 Django + AngularJS
Django - 403 禁止。 CSRF 令牌丢失或不正确