Django 1.11.6版です
前の記事でCSRF周りの内容がスッポリ抜けていたので、追加メモです。
送信フォームでの設定
基本中の基本ですね。テンプレートの中に{% csrf_token %}を入れておけば終わりです。
<form action="auth" method="post">
{% csrf_token %}
実際のHTMLでは{% csrf_token %}が
<input type='hidden' name='csrfmiddlewaretoken' value='値' />
と展開されます。
Ajaxでの設定
TypeScriptで書いていますが、ほとんどJavaScriptそのままで動くかと思います。ただしfindのところでUnderscore使ってますので、使わない場合はfindの代わりの実装が必要です。
Djangoからページを取得した時のHTTPレスポンスのcookieにCSRFの情報が格納されています。
JavaScriptからはdocument.cookieよりcsrftokenというパラメータ名で取得できるので、POSTする時にcsrftokenの内容をX-CSRFTokenという名前でHTTPのヘッダに設定して送り返します。
let xhr = new XMLHttpRequest();
xhr.open("POST", "api");
// 送るデータの設定
let params:any = {
};
// for Django
// ------------------------------------------------
let CSRF_KEY_NAME="csrftoken=";
let csrf = _.find(document.cookie.split(";"), (cookie)=>{
return cookie.trim().substr(0, CSRF_KEY_NAME.length) == CSRF_KEY_NAME;
})
if(csrf) xhr.setRequestHeader("X-CSRFToken", csrf.trim().substr(CSRF_KEY_NAME.length));
// ------------------------------------------------
xhr.setRequestHeader("Content-Type", "application/json");
xhr.send(JSON.stringify(params));
※document.cookieは;区切り。;の後にスペースもあるのでtrimして使ってます
なお、ここではあまり関係がないのですがついでなので……
Pythonでfind
next((val for val in リスト if 条件), デフォルト値)
で、最初の条件にマッチしたリストの中身を返し、リストに無ければデフォルト値を返します。
タプルの内包表記はPython3だと遅延評価になるので(generatorが返る)速度も優秀です。
テストでCSRFを無効にする
テストの時はCSRF認証を止めてPOSTすることができます。
from django.test.client import Client
client = Client(enforce_csrf_checks=False)
※公式ドキュメント読んだらデフォルトがFalseだとか(´・ω・`)ジツハイラナイ
# パスワード無し時の動作テスト
result = client.post('/account/auth', {"user":"unknown_user"})
self.assertEqual(result.status_code, 403)