How to retrieve/provide a CSRF token to/from Django as an API

爷,独闯天下 提交于 2019-12-07 06:24:37

问题


I'm working on a project that uses the Django REST Framework as a backend (let's say at api.somecompany.com but has a React.js frontend (at www.somecompany.com) not served by Django that makes AJAX requests.

I can't, therefore, use Django's traditional method of having the template include the CSRF token like this <form action="." method="post">{% csrf_token %}

I can make a request to Django REST Framework's api-auth\login\ url, which will return this header: Set-Cookie:csrftoken=tjQfRZXWW4GtnWfe5fhTYor7uWnAYqhz; expires=Mon, 01-Aug-2016 16:32:10 GMT; Max-Age=31449600; Path=/ - but I can't then retrieve this cookie to send back with my AJAX requests with X-CSRFToken (my understanding is of the separate subdomain), and it doesn't seem to be included automatically.

Here's my relevant code:

// using jQuery
function getCookie(name) {
    var cookieValue = null;
    if (document.cookie && document.cookie != '') {
        var cookies = document.cookie.split(';');
        for (var i = 0; i < cookies.length; i++) {
            var cookie = jQuery.trim(cookies[i]);
            // 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;
}

function csrfSafeMethod(method) {
    // these HTTP methods do not require CSRF protection
    return (/^(GET|HEAD|OPTIONS|TRACE)$/.test(method));
}
$.ajaxSetup({
    beforeSend: function(xhr, settings) {
        if (!csrfSafeMethod(settings.type)) {
            xhr.setRequestHeader("X-CSRFToken", getCookie('csrftoken'));
        }
    }
});

As the page loads I call this to make sure I have a token:

$.ajax(loginUrl, { method: "OPTIONS", async: false })
    .done(function(data, textStatus, jqXHR) {
        console.log(jqXHR)
        app.csrftoken@ = $.cookie("csrftoken")
        console.log($.cookie("csrftoken"))
        console.log(app.csrftoken)
    })
    .fail(function(jqXHR, textStatus, errorThrown) {
        console.log(jqXHR)
    });

This isn't exactly clean but I haven't proven the concept to myself yet.

What is the 'correct' way of authenticating / protecting against CSRF when the frontend and backend are on different ports/domains?


回答1:


You can, as a matter of fact, make this work with CSRF protection, using a whitelist of allowed request origins.

To make this work, you're going to have to use Cross-Origin Resource Sharing (CORS). To achieve this, I'd recommend creating a middleware class that sets up your cross-sharing credentials. There's a great gist that outlines how to use some of the build in cross origin HTTP request headers. You can, if you want, insert the CSRF token via middleware in this way, by changing the origin reference values for the request.

The django-cors-headers app does this for you. You can see how they've negotiated the CSRF tokens in their middleware file, if you're interested.

Refer to the Django REST CORS Docs for more on this (they recommend using django-cors-headers).

If you're still having difficulties, try:

  1. setting the crossDomain parameter of your AJAX request to True. Sometimes, jQuery won't process the request if this isn't specified.
  2. if you're still not receiving a token header in the request at all, try appending the ensure_csrf_cookie() decorator around your view method. Occasionally, when there's no {% csrf_token %} template tag on the page (if you're not rendering a form), Django won't include the token in the request at all.


来源:https://stackoverflow.com/questions/31792770/how-to-retrieve-provide-a-csrf-token-to-from-django-as-an-api

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!