【问题标题】:POST method always return 403 ForbiddenPOST 方法总是返回 403 Forbidden
【发布时间】:2012-05-26 15:59:42
【问题描述】:

我已阅读 Django - CSRF verification failed 以及与 django 和 POST 方法相关的几个问题(和答案)。对我来说最好但不工作的答案之一是https://stackoverflow.com/a/4707639/755319

所有获得批准的答案都表明至少 3 件事:

  1. 使用RequestContext作为render_to_response_call的第三个参数
  2. 使用 POST 方法在每个表单中添加 {% csrf_token %}
  3. 检查 settings.py 中的 MIDDLEWARE_CLASSES

我完全按照建议做了,但错误仍然出现。我使用 django 1.3.1(来自 ubuntu 12.04 存储库)和 python 2.7(默认来自 ubuntu)

这是我的观点:

# Create your views here.
from django.template import RequestContext
from django.http import HttpResponse
from django.shortcuts import render_to_response
from models import BookModel

def index(request):
    return HttpResponse('Welcome to the library')

def search_form(request):
    return render_to_response('library/search_form.html')

def search(request):
    if request.method=='POST':
        if 'q' in request.POST:
            q=request.POST['q']
            bookModel = BookModel.objects.filter(title__icontains=q)
            result = {'books' : bookModel,}
            return render_to_response('library/search.html', result, context_instance=RequestContext(request))
        else:
            return search_form(request)
    else:
        return search_form(request)

这是我的模板(search_form.html):

{% extends "base.html" %}
{% block content %}
<form action="/library/search/" method="post">
    {% csrf_token %} 
    <input type="text" name="q">
    <input type="submit" value="Search">
</form>
{% endblock %}

我已经重启了服务器,但是403禁止错误仍然存​​在,提示CSRF验证失败。

我有 2 个问题:

  1. 如何解决此错误?
  2. 为什么在 django 中做一个“POST”这么难,我的意思是有什么具体的原因让它如此冗长(我来自 PHP,之前从未发现过这样的问题)?

【问题讨论】:

    标签: django post csrf django-csrf


    【解决方案1】:

    我可能错了,但是我发现上述解决方案相当复杂。

    对我有用的只是将我的 csrf 令牌包含到我的发布请求中。

    $.ajax({
        type: "POST",
        url: "/reports/",
        data: { csrfmiddlewaretoken: "{{ csrf_token }}",   // < here 
                state:"inactive" 
              },
        success: function() {
            alert("pocohuntus")
            console.log("prototype")
        }
    })
    

    【讨论】:

      【解决方案2】:

      此答案适用于将来可能遇到同样问题的人。

      Django 中表单所需的 CSRF {{csrf_token}} 模板标签可防止跨站点请求伪造。 CSRF 使客户端浏览器访问过的恶意站点可以向您自己的服务器发出请求。因此,django 提供的 csrf_token 使您的 django 服务器和站点可以很容易地被保护免受这种类型的恶意攻击。如果您的表单不受 csrf_token 保护,django 会返回 403 禁止页面。这是对您网站的一种保护形式,尤其是在没有故意遗漏令牌的情况下。

      但在某些情况下,django 站点不想使用 csrf_token 保护其表单。例如,我开发了一个 USSD 应用程序,需要一个视图函数来接收来自 USSD API 的 POST 请求。我们应该注意到 POST 请求不是来自客户端上的表单,因此不可能存在 CSRF 风险,因为恶意站点无法提交请求。 POST 请求是在用户拨打 USSD 代码时收到的,而不是在提交表单时收到的。

      换句话说,在某些情况下,函数需要获取 POST 请求而不需要 {{csrf_token}}。

      Django 为我们提供了一个装饰器@csrf_exempt。此装饰器将视图标记为不受中间件确保的保护。

      from django.views.decorators.csrf import csrf_exempt
      from django.http import HttpResponse
      
      @csrf_exempt
      def my_view(request):
          return HttpResponse('Hello world')
      

      Django 还提供了另一个装饰器,它与{{csrf_token}} 执行相同的功能,但它不会拒绝传入的请求。这个装饰器是@requires_csrf_token。例如:

      @requires_csrf_token
      def my_view(request):
          c = {}
          # ...
          return render(request, "a_template.html", c)
      

      本文将提到的最后一个装饰器与 {{csrf_token}} 完全相同,称为@csrf_protect。但是,单独使用这个装饰器并不是最佳实践,因为您可能会忘记将它添加到您的视图中。例如:

      @csrf_protect
      def my_view(request):
          c = {}
          # ...
          return render(request, "a_template.html", c)
      

      以下是一些可以更好地指导和解释的链接。

      https://docs.djangoproject.com/en/1.7/ref/contrib/csrf/#module-django.views.decorators.csrf

      https://docs.djangoproject.com/en/1.7/ref/contrib/csrf/

      http://www.squarefree.com/securitytips/web-developers.html#CSRF

      【讨论】:

        【解决方案3】:

        避免此类问题的最简单方法是使用render 快捷方式。

        from django.shortcuts import render
        # .. your other imports
        
        def search_form(request):
            return render(request, 'library/search_form.html')
        
        def search(request):
            q = request.GET.get('q')
            results = BookModel.objects.all()
            if q:
                results = results.filter(title__icontains=q)
            return render(request, 'library/search.html', {'result': results})
        

        【讨论】:

        • 必须尽快尝试一下,看起来很清楚,谢谢您的回答。使用这种方法有什么缺点吗?我想知道如果有如此清晰的语法,为什么文档和 djangobook 会提供如此冗长的语法
        • 我知道这是一个旧线程,但我更新了它以进一步阐明搜索方法。希望这现在(更多)有用。
        • 另外,搜索方法可能是唯一应该使用 GET 而不是 POST 的方法,因为它们不会改变任何东西。
        【解决方案4】:

        尝试将 RequestContext 放入 search_form 视图的 render_to_response:

        context_instance=RequestContext(request)
        

        【讨论】:

        • 可以,谢谢你的回答。但是如何以及为什么?你能解释一下吗?
        • 因为csrf_token 必须在您的视图中创建,以便 django 可以将其传递给模板。在您的情况下,由于您的搜索视图没有创建令牌,因此模板中的 {% csrf_token %}empty string (None) 并且结果页面在验证时失败
        【解决方案5】:

        响应是 403 bcoz, django 在您发出的每个 POST 请求中都需要一个 csrf 令牌(包含在发布数据中)。

        有多种方法可以做到这一点,例如:

        从cookie中获取token及方法在enter link description here文章中有说明

        您可以使用模板中的 {{ csrf_token }} 从 DOM 访问它

        所以现在使用第二种方法:

        var post_data = {
          ...
          'csrfmiddlewaretoken':"{{ csrf_token }}"
          ...
        }
        $.ajax({
          url:'url',
          type:'POST'
          data:post_data,
          success:function(data){
            console.log(data);
          },
          error:function(error){
            console.log(error);
          }
        });
        

        【讨论】:

          【解决方案6】:

          您需要在回复中使用RequestContext

          例如 在view.py 文件中

          from django.template import RequestContext
          
          def home(request):
              return render_to_response('home.html',RequestContext(request, {}))
          

          【讨论】:

            【解决方案7】:

            你也可以使用

            direct_to_template(request, 'library/search.html', result) 
            

            而不是

            render_to_response('library/search.html', result, context_instance=RequestContext(request))
            

            因为direct_to_template 会自动添加RequestContext。但请注意,direct_to_template 将被弃用,django 提议使用 CBV TemplateView 代替。

            RequestContext 允许您使用上下文处理器。这是你的错误:{% csrf_token %} 输出空字符串,你得到 403。

            【讨论】:

            • 嗨,谢谢你的评论,看来我在熟悉 django 之前应该考虑很多事情:D
            【解决方案8】:

            这为我解决了这个问题。正如 Django 文档所说,在能够直接将 POST 方法发送到您的 RESTAPI 之前,需要完成一些处理。这是代码:

            function getCookie(name) {
                let cookieValue = null;
                if (document.cookie && document.cookie !== '') {
                    const cookies = document.cookie.split(';');
                    for (let i = 0; i < cookies.length; i++) {
                        const cookie = cookies[i].trim();
                        // Does this cookie string begin with the name we want?
                        if (cookie.substring(0, name.length + 1) === (name + '=')) {
                            cookieValue = decodeURIComponent(cookie.substring(name.length + 1));
                            break;
                        }
                    }
                }
                return cookieValue;
            }
            
            const csrftoken = getCookie('csrftoken');
            console.log(csrftoken)
            
            
            
            
            
            $.ajax({
                method:'POST',
                url:"//127.0.0.1:8000/app/homepage/", /*change it to your api endpoint*/
                headers: {'X-CSRFToken': csrftoken},
                data : { "data" : "3"}
                
               
            })
            

            我无法用这个解决方案保证的一件事是安全方面

            【讨论】:

              猜你喜欢
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 2023-02-24
              • 1970-01-01
              • 2023-01-20
              • 1970-01-01
              • 1970-01-01
              • 2017-05-15
              相关资源
              最近更新 更多