POST方法总是返回403 Forbidden

时间:2012-05-19 08:01:43

标签: django post csrf django-csrf

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

所有批准的答案都至少提出了3件事:

  1. 使用RequestContext作为render_to_response_call
  2. 的第三个参数
  3. 使用POST方法
  4. 在每个表单中添加{%csrf_token%}
  5. 检查settings.py
  6. 中的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验证失败。

    我有两个问题:

    1. 如何解决此错误?
    2. 为什么在django中制作“POST”这么难,我的意思是有什么特别的理由让它变得如此冗长(我来自PHP,以前从未发现过这样的问题)?

8 个答案:

答案 0 :(得分:11)

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

对我有用的只是将我的csrf令牌包含在我的帖子请求中。

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

答案 1 :(得分:5)

避免此类问题的最简单方法是使用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})

答案 2 :(得分:3)

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

context_instance=RequestContext(request)

答案 3 :(得分:3)

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

Django中表单所需的CSRF {{csrf_token}}模板标记可防止跨站点请求伪造。 CSRF使得客户端浏览器访问过的恶意站点可以向您自己的服务器发出请求。因此,django提供的csrf_token使您的django服务器和站点免受此类恶意攻击的影响变得简单。如果您的表单不受csrf_token保护,则django将返回403禁止页面。这是对您网站的一种保护形式,特别是在故意不遗漏令牌时。

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

换句话说,在某些情况下,函数需要获取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

答案 4 :(得分:2)

回复是403 bcoz,  django在你发出的每个POST请求中都需要一个csrf令牌(包含在帖子数据中)。

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

在文章enter link description here

中解释了从cookie获取令牌和方法

您可以使用模板中提供的{{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);
  }
});

答案 5 :(得分:0)

您也可以使用

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。

答案 6 :(得分:0)

您需要在回复中使用RequestContext

例如,

view.py文件中

from django.template import RequestContext

def home(request):
    return render_to_response('home.html',RequestContext(request, {}))

答案 7 :(得分:0)

这为我解决了它。正如 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"}
    
   
})

对于此解决方案,我无法保证的一件事是安全方面