簡體   English   中英

使用 Django 的 CSRF,使用 Axios 的 React+Redux

[英]CSRF with Django, React+Redux using Axios

這是一個教育項目,不是為了生產。 我不打算讓用戶登錄作為其中的一部分。

我可以在沒有用戶登錄的情況下使用 CSRF 令牌對 Django 進行 POST 調用嗎? 我可以在不使用 jQuery 的情況下做到這一點嗎? 我在這里超出了我的深度,並且肯定會混淆一些概念。

對於 JavaScript 方面,我找到了這個redux-csrf包。 我不確定如何使用 Axios 將它與我的POST操作結合起來:

export const addJob = (title, hourly, tax) => {
  console.log("Trying to addJob: ", title, hourly, tax)
  return (dispatch) => {
    dispatch(requestData("addJob"));
    return axios({
      method: 'post',
      url: "/api/jobs",
      data: {
        "title": title,
        "hourly_rate": hourly,
        "tax_rate": tax
      },
      responseType: 'json'
    })
      .then((response) => {
        dispatch(receiveData(response.data, "addJob"));
      })
      .catch((response) => {
        dispatch(receiveError(response.data, "addJob"));
      })
  }
};

在 Django 方面,我已經閱讀了有關 CSRF 的文檔,並且通常使用基於類的視圖。

到目前為止,這是我的觀點:

class JobsHandler(View):

    def get(self, request):
        with open('./data/jobs.json', 'r') as f:
            jobs = json.loads(f.read())

        return HttpResponse(json.dumps(jobs))

    def post(self, request):
        with open('./data/jobs.json', 'r') as f:
            jobs = json.loads(f.read())

        new_job = request.to_dict()
        id = new_job['title']
        jobs[id] = new_job

        with open('./data/jobs.json', 'w') as f:
            f.write(json.dumps(jobs, indent=4, separators=(',', ': ')))

        return HttpResponse(json.dumps(jobs[id]))

我嘗試使用csrf_exempt裝飾器只是為了暫時不必擔心這一點,但這似乎不是它的工作原理。

我已將{% csrf_token %}添加到我的模板中。

這是我的getCookie方法(從 Django 文檔中竊取):

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 = 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;
}

我讀到我需要更改 Axios CSRF 信息:

var axios = require("axios");
var axiosDefaults = require("axios/lib/defaults");

axiosDefaults.xsrfCookieName = "csrftoken"
axiosDefaults.xsrfHeaderName = "X-CSRFToken"

我在哪里粘貼實際令牌,我通過調用getCookie('csrftoken')獲得的值?

有三種方法。 您可以在每個axios調用的標頭中手動包含令牌,您可以在每次調用中設置axios的xsrfHeaderName ,或者設置默認的xsrfHeaderName

1.手動添加

假設您已將令牌的值存儲在名為csrfToken的變量中。 在axios調用中設置標題:

// ...
method: 'post',
url: '/api/data',
data: {...},
headers: {"X-CSRFToken": csrfToken},
// ...

2.在調用中設置xsrfHeaderName

添加這個:

// ...
method: 'post',
url: '/api/data',
data: {...},
xsrfHeaderName: "X-CSRFToken",
// ...

然后在settings.py文件中添加以下行:

CSRF_COOKIE_NAME = "XSRF-TOKEN"

3.設置默認標題[1]

您可以為axios設置默認標頭,而不是在每次調用中定義標頭。

在您要導入axios以進行調用的文件中,在導入下方添加以下內容:

axios.defaults.xsrfHeaderName = "X-CSRFToken";

然后在settings.py文件中添加以下行:

CSRF_COOKIE_NAME = "XSRF-TOKEN"

編輯2017年6月10日 ):用戶@yestema說它與Safari [2]略有不同

編輯2019年4月17日 ):用戶@GregHolst說上面的Safari解決方案對他不起作用。 相反,他在MacOS Mojave上使用上述解決方案#3 for Safari 12.1。 來自評論

編輯2019年2月17日 ):您可能還需要設置[3]

axios.defaults.withCredentials = true

問題:下一部分對任何人都有用嗎? 我想知道只有包含解決方案才能改善這個答案。 如果您有意見,請告訴我。

困惑:

Django Docs

首先,詹姆斯·埃文斯引用Django文章的全文如下:

...在每個XMLHttpRequest上,將自定義X-CSRFToken標頭設置為CSRF標記的值。 這通常更容易,因為許多JavaScript框架提供了允許在每個請求上設置標頭的鈎子。

作為第一步,您必須自己獲取CSRF令牌。 建議的令牌來源是csrftoken cookie,如果您已按照上面的說明為視圖啟用了CSRF保護,則會設置該cookie。

注意

默認情況下,CSRF令牌cookie名為csrftoken,但您可以通過CSRF_COOKIE_NAME設置控制Cookie名稱。

默認情況下,CSRF標頭名稱為HTTP_X_CSRFTOKEN,但您可以使用CSRF_HEADER_NAME設置對其進行自定義。


Axios Docs

這來自Axios文檔 它表示您設置包含csrftoken的cookie的名稱,以及此處的標頭名稱:

  // `xsrfCookieName` is the name of the cookie to use as a value for xsrf token
  xsrfCookieName: 'XSRF-TOKEN', // default

  // `xsrfHeaderName` is the name of the http header that carries the xsrf token value
  xsrfHeaderName: 'X-XSRF-TOKEN', // default

條款

如我的問題所示,您可以使用document.cookie訪問cookie。 我唯一的cookie是我放在Django模板中的CSRF令牌。 這是一個例子:

csrftoken=5knNceCUi9nL669hGGsvCi93XfqNhwTwM9Pev7bLYBOMXGbHVrjitlkKi44CtpFU

在那些令人困惑的文檔中有一些概念被拋出:

  • 包含CSRF令牌的cookie的名稱。 在Django中,這是默認的csrftoken ,它位於csrftoken號的左側。
  • 實際的令牌。 這是cookie中等號右側的所有內容。
  • 帶有標記值的http標頭。

事情我想,沒有工作: 12

我發現, axios.defaults.xsrfCookieName = "XCSRF-TOKEN"; CSRF_COOKIE_NAME = "XCSRF-TOKEN"

在Mac OS上不適用於APPLE Safari

MAC Safari的解決方案很簡單, 只需將XCSRF-TOKEN更改為csrftoken

所以,在js-code中應該是:

    import axios from 'axios';
    axios.defaults.xsrfHeaderName = "X-CSRFTOKEN";
    axios.defaults.xsrfCookieName = "csrftoken";

在settings.py中:

    CSRF_COOKIE_NAME = "csrftoken"

這個配置對我來說沒有問題Config axios CSRF django

 import axios from 'axios' /** * Config global for axios/django */ axios.defaults.xsrfHeaderName = "X-CSRFToken" axios.defaults.xsrfCookieName = 'csrftoken' export default axios 

在花了太多時間研究並實施上述答案之后,我發現我的錯誤是這個問題! 我已添加此答案以補充已接受的答案。 我已經按照上述設置了所有內容,但對我來說,問題實際上在於瀏覽器本身!

如果在本地進行測試,請確保您通過127.0.0.1而不是localhost訪問 react! localhost以不同的方式處理請求標頭,並且不會在標頭響應中顯示 CSRF 令牌,而127.0.0.1會! 所以代替localhost:3000試試127.0.0.1:3000

希望這可以幫助。

“簡單的方式”幾乎對我有用。 這似乎有效:

import axios from 'axios';
axios.defaults.xsrfHeaderName = "X-CSRFTOKEN";
axios.defaults.xsrfCookieName = "XCSRF-TOKEN";

並在settings.py文件中:

CSRF_COOKIE_NAME = "XCSRF-TOKEN"

您可以手動將Django提供的CSRF令牌添加到您的所有帖子請求中,但這很煩人。

來自Django文檔

雖然上述方法( 手動設置CSRF令牌 )可用於AJAX POST請求,但它有一些不便之處:您必須記住在每次POST請求時都將CSRF令牌作為POST數據傳遞。 因此,有一種替代方法:在每個XMLHttpRequest上,將自定義X-CSRFToken標頭設置為CSRF標記的值。 這通常更容易,因為許多JavaScript框架提供了允許在每個請求上設置標頭的鈎子。

文檔具有可用於從CSRF令牌cookie中提取CSRF令牌的代碼,然后將其添加到AJAX請求的標頭中。

實際上有一種非常簡單的方法可以做到這一點。

添加axios.defaults.xsrfHeaderName = "X-CSRFToken"; 到您的應用配置,然后在settings.py文件中設置CSRF_COOKIE_NAME = "XSRF-TOKEN" 奇跡般有效。

對我來說,django沒有聽我發送的標題。 我可以卷入api但無法使用axios訪問它。 查看cors-headers包 ...它可能是你最好的朋友。

我通過安裝django-cors-headers來修復它

pip install django-cors-headers

然后加入

INSTALLED_APPS = (
    ...
    'corsheaders',
    ...
)

MIDDLEWARE = [  # Or MIDDLEWARE_CLASSES on Django < 1.10
    ...
    'corsheaders.middleware.CorsMiddleware',
    'django.middleware.common.CommonMiddleware',
    ...
]

進入我的settings.py

我也有

ALLOWED_HOSTS = ['*']
CORS_ORIGIN_ALLOW_ALL = True
CORS_ALLOW_CREDENTIALS = True
CORS_EXPOSE_HEADERS = (
    'Access-Control-Allow-Origin: *',
)

在我的settings.py雖然這可能是矯枉過正

除了yestema所說的(並且由krescruz,cran_man,Dave Merwin等人回應),您還需要:

axios.defaults.withCredentials = true

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM