DjangoのミドルウェアにCSRFミドルウェア(django.middleware.csrf.CsrfViewMiddleware
)を指定していると、
csrf_token
を渡していないPOSTメソッドは403エラーになってしまいます。
ただし、外部からのアクセスを可能にしたい場合など、特定のViewのメソッドに対しては
このCSRF対策を無効にしたい場合がありますよね。
その方法を調べたのでメモ。
はじめに
403エラーが発生する例を見てみます。
settings.py(一部)
MIDDLEWARE_CLASSES = (
'django.middleware.csrf.CsrfViewMiddleware',
)
template.html
<html> <head> </head> <body> <form action="/view1/" method="post"> <!-- {% csrf_token %} --> <div><input type="submit" value="Class-based View1(FAIL)"></div> </form> </body> </html>
urls.py
from django.conf.urls import patterns from django.views.generic import TemplateView from .views import CsrfView1 urlpatterns = patterns('', (r'^view1/', CsrfView1.as_view()), (r'^$', TemplateView.as_view(template_name='template.html')), )
views.py
from django.http import HttpResponse from django.views.generic.base import View class CsrfView1(View): def post(self, request): return HttpResponse('ok')
この例では、http://localhost:8080にアクセスし
ボタンをクリックすると、
というエラーが発生します。
これを避けるためには<form>
タグ内でcsrf_token
テンプレートタグを使用します。
(template.html
のコメントアウトしている部分のコメントを外すと、エラーは表示されなくなります。)
CSRF対策を無効にする方法
CSRF対策のチェックを無効にするには、@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')
でいいと書かれていますが、対象が今回のようなクラスベースビュー(Class-based views)の場合は注意が必要です。
具体的には、以下のような書き方をします。
from django.http import HttpResponse from django.views.decorators.csrf import csrf_exempt from django.views.generic.base import View from django.utils.decorators import method_decorator class CsrfView2(View): def post(self, request): return HttpResponse('ok') @method_decorator(csrf_exempt) def dispatch(self, *args, **kwargs): return super(CsrfView2, self).dispatch(*args, **kwargs)
ポイントは
csrf_exempt
デコレータはdispatch
メソッドにつけるcsrf_exempt
をmethod_decorator
メソッドでラップする
補足
クラスベースビューの場合にどうしてこのような書き方になるのか?という部分について
公式ドキュメントには次のように記載されています。
(日本語URL) http://docs.djangoproject.jp/en/latest/topics/class-based-views.html
(英語URL) https://docs.djangoproject.com/en/1.5/topics/class-based-views/intro/#decorating-the-class
以下、ドキュメントより引用。
To decorate every instance of a class-based view, you need to decorate the class definition itself. To do this you apply the decorator to the
dispatch()
method of the class.
A method on a class isn’t quite the same as a standalone function, so you can’t just apply a function decorator to the method – you need to transform it into a method decorator first. Themethod_decorator
decorator transforms a function decorator into a method decorator so that it can be used on an instance method.
日本語版では
クラスベースビューの全インスタンスをデコレートするには、クラスの定義そのものをデコレートします。クラスの
dispatch()
メソッドにデコレータを適用することでできます。
クラスメソッドは単なる関数とは違います。メソッドに関数デコレータを適用することはできません。関数デコレータをメソッドデコレータに変換する必要があります。method_decorator
デコレータは関数デコレータをメソッドデコレータに変換するので、これをインスタンスメソッドで使えます。
一段落目はクラスベースビューのデコレータはdispatch()
メソッドに適用してくださいねーということが書いてますね。
二段落目は、クラスに定義している "メソッド(method
)" と一般的な "関数(function
)" は同じものではないので、適用したいデコレータの変換が必要だと述べています。
ただし、検証したところXXXView.as_view()
という形で使っている限りでは、@method_decorator
はなくてもエラーにはなりませんでした。
from django.http import HttpResponse from django.views.decorators.csrf import csrf_exempt from django.views.generic.base import View from django.utils.decorators import method_decorator class CsrfView3(View): def post(self, request): return HttpResponse('ok') # これでもうまくいく @csrf_exempt def dispatch(self, *args, **kwargs): return super(CsrfView3, self).dispatch(*args, **kwargs)
リファレンス
http://blog.joshcrompton.com/2012/09/how-to-turn-off-csrf-protection-for.html
以下は日本語Django公式リファレンス
http://docs.djangoproject.jp/en/latest/ref/contrib/csrf.html
http://docs.djangoproject.jp/en/latest/topics/class-based-views.html