7

Django Rest Framework を使用してアプリケーションの API を構築していますが、トークン認証用に DjangoRestFramework-JWT を実装したいと考えています。手順は単純に見えますが、エンドポイントをテストすると 500 エラーが発生します。端末出力は、csrf_token が提供されなかったことを示す大量の html です。コードとエラーは以下のとおりです。よろしくお願いいたします。

curl -X POST -d "username=admin&password=123abc" http://127.0.0.1:8000/api/token/auth/

CSRF エラー

<!DOCTYPE html>
<html lang="en">
    <head>
      <meta http-equiv="content-type" content="text/html; charset=utf-8">
      <meta name="robots" content="NONE,NOARCHIVE">
      <title>403 Forbidden</title>
    </head>
    <body>
        <div id="summary">
            <h1>Forbidden <span>(403)</span></h1>
            <p>CSRF verification failed. Request aborted.</p>
            <p>You are seeing this message because this site requires a CSRF cookie when submitting forms. This cookie is required for security reasons, to ensure that your browser is not being hijacked by third parties.</p>
            <p>If you have configured your browser to disable cookies, please re-enable them, at least for this site, or for &#39;same-origin&#39; requests.</p>
        </div>

        <div id="info">
            <h2>Help</h2>
            <p>Reason given for failure:</p>
            <pre>CSRF cookie not set.</pre>
            <p>In general, this can occur when there is a genuine Cross Site Request Forgery, or when <a href="https://docs.djangoproject.com/en/1.8/ref/csrf/">Django's CSRF mechanism</a> has not been used correctly.  For POST forms, you need to ensure:</p>
            <ul>
                <li>Your browser is accepting cookies.</li>
                <li>The view function passes a <code>request</code> to the template's <a href="https://docs.djangoproject.com/en/dev/topics/templates/#django.template.backends.base.Template.render"><code>render</code></a> method.</li>
                <li>In the template, there is a <code>{% csrf_token %}</code> template tag inside each POST form that targets an internal URL.</li>
                <li>If you are not using <code>CsrfViewMiddleware</code>, then you must use <code>csrf_protect</code> on any views that use the <code>csrf_token</code> template tag, as well as those that accept the POST data.</li>
            </ul>

            <p>You're seeing the help section of this page because you have <code>DEBUG = True</code> in your Django settings file. Change that to <code>False</code>, and only the initial error message will be displayed.  </p>
            <p>You can customize this page using the CSRF_FAILURE_VIEW setting.</p>
        </div>
    </body>
</html>

設定.py

INSTALLED_APPS = (
    ...
    'rest_framework',
    ...
)

REST_FRAMEWORK = {
    'DEFAULT_PERMISSION_CLASSES': (
        'rest_framework.permissions.IsAuthenticated',
    ),
    'DEFAULT_AUTHENTICATION_CLASSES': (
        'rest_framework.authentication.SessionAuthentication',
        'rest_framework.authentication.BasicAuthentication',
        'rest_framework_jwt.authentication.JSONWebTokenAuthentication',
    ),
}

JWT_AUTH = {
    'JWT_RESPONSE_PAYLOAD_HANDLER':
        'app.utils.jwt_response_payload_handler',
    'JWT_EXPIRATION_DELTA': datetime.timedelta(seconds=30000),
}

グローバル Urls.py

urlpatterns = patterns(
    ...
    url(r'^api/auth/token/$', 'rest_framework_jwt.views.obtain_jwt_token'),
    url(r'^api/auth/', include('rest_framework.urls', namespace='rest_framework')),
    url(r'^api/', include(router.urls)),
)
4

1 に答える 1

7

SessionAuthenticationDRF 設定で定義されたクラスが原因で、このエラーが発生しています。

DRFSessionAuthenticationは、CSRF のチェックを必要とする認証に Django のセッション フレームワークを使用します。 DRF は、認証されたユーザーに対して CSRF チェックを実施します。これは、認証されたリクエストのみが CSRF トークンを必要とし、匿名のリクエストは CSRF トークンなしで送信できることを意味します。

ここで、認証されたユーザーからリクエストを行いDEFAULT_AUTHENTICATION_CLASSESsettings.pyファイルで定義された順序に従って、SessionAuthentication最初に呼び出されます。リクエストが認証されたユーザーからのものかどうかを確認し、そうである場合はCSRF、リクエスト ヘッダーにトークンが設定されている必要があります。CSRF Tokenリクエストを送信していないため、エラーが返されます。

リクエストの作成中にヘッダーに CSRF トークンを渡す必要があります。オプションを介してそれを行うことができ--header/-Hます。

--header "X-CSRFToken: {token_value}" # using '--header'
-H "X-CSRFToken: {token_value}" # using '-H' 

リクエストを次のように変更できます。

curl --header "X-CSRFToken: {token_value}" -X POST -d "username=admin&password=123abc" http://127.0.0.1:8000/api/token/auth/

別の方法:

認証済みユーザーにこの CSRF チェックを適用しないカスタム セッション認証クラスを作成できる別のオプションがあります。

SessionCsrfExemptAuthentication以下のクラスは、認証されたユーザーに対して CSRF トークン チェックを強制しません。

from rest_framework.authentication import SessionAuthentication 

class SessionCsrfExemptAuthentication(SessionAuthentication):

    def enforce_csrf(self, request):
        return  # To not perform the csrf check previously happening

次に、設定でこの認証クラスを次のように定義できます。

'DEFAULT_AUTHENTICATION_CLASSES': (
        # Replace DRF session authentication class with our custom authentication class
        'rest_framework.authentication.SessionCsrfExemptAuthentication',
        'rest_framework.authentication.BasicAuthentication',
        'rest_framework_jwt.authentication.JSONWebTokenAuthentication',
    ),
于 2015-07-29T14:48:57.057 に答える