web-dev-qa-db-ja.com

django-rest-frameworkは、AllowAnyアクセス許可にもかかわらず、POST、PUT、DELETEで403応答を返します

Django-oneall を使用して、サイトでソーシャルログインセッション認証を許可しています。 Django-rest-frameworkに推奨される認証プロバイダーの1つではありませんが、rest_framework.authentication.SessionAuthenticationはDjangoのデフォルトのセッション認証を使用します。だから統合はかなり簡単なはずだと思いました。

アクセス許可の面では、最終的にはIsAdminを使用しますが、開発目的のためにIsAuthenticatedに設定しただけです。それが403を返すとき、私はAllowAnyへのアクセス許可を緩和しましたが、まだサイコロはありませんでした。これが私の残りのフレームワーク設定です:

settings.py

REST_FRAMEWORK = {
    'DEFAULT_AUTHENTICATION_CLASSES': (
        'rest_framework.authentication.SessionAuthentication',
    ),
    'DEFAULT_PERMISSION_CLASSES': (
        'rest_framework.permissions.AllowAny',
        # 'rest_framework.permissions.IsAuthenticated',
        # 'rest_framework.permissions.IsAdminUser',
     ),
    'PAGE_SIZE': 100,
    'DEFAULT_FILTER_BACKENDS': (
        'rest_framework.filters.DjangoFilterBackend',
    ),
}

編集:

私は以下の答えに基づいてこれを機能させました。 rest_frameworkcsrftoken cookieと同じ値のX-CSRFTokenヘッダーの両方を期待していることがわかりました。すべてのajaxリクエストに対してヘッダーを送信するようにフロントエンドコードを設定し、すべてが機能しました大丈夫。

13
ckot

Django RESTフレームワークは、いくつかの関連する状況下でステータスコード403を返します:

  • 必要な権限レベルがない場合(例:DEFAULT_PERMISSION_CLASSES('rest_framework.permissions.IsAuthenticated',)の場合に、認証されていないユーザーとしてAPIリクエストを行う。
  • 安全でない要求タイプ(POST、PUT、PATCHまたはDELETE-副作用があるはずの要求)を実行している場合、rest_framework.authentication.SessionAuthenticationを使用していて、CSRFTokenをリクエストセットに含めていません。
  • 安全でないリクエストタイプを実行していて、含まれているCSRFTokenが無効になっている場合。

テストAPIに対していくつかのデモリクエストを行い、それぞれの例を示して、発生している問題を診断し、解決方法を示します。 requestsライブラリを使用します。

テストAPI

単一のフィールド(Life、デフォルト値は42)を含む単一のモデルanswerを使用して、非常に単純なDRF APIをセットアップしました。これからのすべてはかなり簡単です。 ModelSerializer-LifeSerializerModelViewSet-LifeViewSetDefaultRouter/life URLルートに設定しました。 APIを使用し、SessionAuthenticationを使用するためにユーザーの認証を要求するようにDRFを構成しました。

APIを終了する

import json
import requests

response = requests.get('http://localhost:8000/life/1/')
# prints (403, '{"detail":"Authentication credentials were not provided."}')
print response.status_code, response.content

my_session_id = 'mph3eugf0gh5hyzc8glvrt79r2sd6xu6'
cookies = {}
cookies['sessionid'] = my_session_id
response = requests.get('http://localhost:8000/life/1/',
                        cookies=cookies)
# prints (200, '{"id":1,"answer":42}')
print response.status_code, response.content

data = json.dumps({'answer': 24})
headers = {'content-type': 'application/json'}
response = requests.put('http://localhost:8000/life/1/',
                        data=data, headers=headers,
                        cookies=cookies)
# prints (403, '{"detail":"CSRF Failed: CSRF cookie not set."}')
print response.status_code, response.content

# Let's grab a valid csrftoken
html_response = requests.get('http://localhost:8000/life/1/',
                             headers={'accept': 'text/html'},
                             cookies=cookies)
cookies['csrftoken'] = html_response.cookies['csrftoken']
response = requests.put('http://localhost:8000/life/1/',
                        data=data, headers=headers,
                        cookies=cookies)
# prints (403, '{"detail":"CSRF Failed: CSRF token missing or incorrect."}')
print response.status_code, response.content

headers['X-CSRFToken'] = cookies['csrftoken']
response = requests.put('http://localhost:8000/life/1/',
                        data=data, headers=headers,
                        cookies=cookies)
# prints (200, '{"id":1,"answer":24}')
print response.status_code, response.content
27
chucksmash

完全を期すために、DRFがコード403を返す状況がもう1つあります。urls.pyファイルのビュー宣言にas_view()を追加し忘れた場合です。ちょうど私に起こり、問題がどこにあるかを見つけるまで何時間も費やしたので、この追加によって誰かの時間を節約できるかもしれません。

2
fzznk

同じ問題を見つける可能性がある人のためだけに。次のようなルーターなしでビューセットを使用している場合:

user_list = UserViewSet.as_view({'get': 'list'})
user_detail = UserViewSet.as_view({'get': 'retrieve'})

Django Restフレームワークは、クラスレベルでpermission_classesを定義しない限り、403を返します。

class UserViewSet(viewsets.ModelViewSet):
    """
    A viewset for viewing and editing user instances.
    """
    permission_classes= YourPermisionClass

それが役に立てば幸い!

1
Duilio