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 令牌丢失或不正确

Django:CSRF 令牌丢失或不正确。 / 避免 % csrf_token %

CSRF 令牌丢失或不正确。 Django + AngularJS

Django - 403 禁止。 CSRF 令牌丢失或不正确

注册表单不起作用(CSRF 令牌丢失或不正确。) django

CSRF 令牌丢失或不正确 - 在 Django 中使用自动完成灯