Как отключить проверку CSRF с помощью PasswordResetView в Django auth?

Я использую Django 3.1 с приложением Django's auth. У меня определено следующее промежуточное ПО

MIDDLEWARE = [
    'django.middleware.security.SecurityMiddleware',
    'django.contrib.sessions.middleware.SessionMiddleware',
    'corsheaders.middleware.CorsMiddleware',
    'django.middleware.common.CommonMiddleware',
    'django.middleware.csrf.CsrfViewMiddleware',
    'django.contrib.auth.middleware.AuthenticationMiddleware',
    'django.contrib.messages.middleware.MessageMiddleware',
    'django.middleware.clickjacking.XFrameOptionsMiddleware',
    'directory.middleware.extend_token_response.ExtendTokenResponse'
]

Я хотел бы использовать функцию сброса пароля Django auth, поэтому я добавил это в представление urls.py

path('reset_password', views.ResetPasswordView.as_view(), name='password_reset'),

и в моем файле views.py я определил

from django.views.decorators.csrf import csrf_exempt
...
class ResetPasswordView(SuccessMessageMixin, PasswordResetView):
    template_name = 'users/password_reset.html'
    email_template_name = 'users/password_reset_email.html'
    subject_template_name = 'users/password_reset_subject'
    success_message = "We've emailed you instructions for setting your password, " \
                      "if an account exists with the email you entered. You should receive them shortly." \
                      " If you don't receive an email, " \
                      "please make sure you've entered the address you registered with, and check your spam folder."
    success_url = reverse_lazy('users-home')

    @csrf_exempt
    def post(self, request, *args, **kwargs):
        email = request.data.get('email')
        try:
            if User.objects.get(email=email).active:
                print("email: %s " % email)
                return super(ResetPasswordView, self).post(request, *args, **kwargs)
        except:
            # this for if the email is not in the db of the system
            return super(ResetPasswordView, self).post(request, *args, **kwargs)

Однако, похоже, что проверка CSRF не проверяется, потому что когда я отправляю запрос, как показано ниже

curl 'http://127.0.0.1:8000/reset_password' \
  -H 'Accept: */*' \
  -H 'Accept-Language: en-US,en;q=0.9' \
  -H 'Connection: keep-alive' \
  -H 'Content-Type: application/json' \
  -H 'Origin: http://localhost:3000' \
  -H 'Referer: http://localhost:3000/' \
  -H 'Sec-Fetch-Dest: empty' \
  -H 'Sec-Fetch-Mode: cors' \
  -H 'Sec-Fetch-Site: cross-site' \
  -H 'User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/108.0.0.0 Safari/537.36' \
  -H 'sec-ch-ua: "Not?A_Brand";v="8", "Chromium";v="108", "Google Chrome";v="108"' \
  -H 'sec-ch-ua-mobile: ?0' \
  -H 'sec-ch-ua-platform: "macOS"' \
  --data-raw '{"username":"abc@dev.com"}' \
  --compressed

Я продолжаю получать 403 ответа с этим сообщением

  <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 “same-origin” requests.</p>

Вам необходимо добавить в URL

urls.py

from django.views.decorators.csrf import csrf_exempt

urlpatterns = [
    path('reset_password', csrf_exempt(views.ResetPasswordView.as_view()), name='password_reset'),
]

django.contrib.auth.views.PasswordResetView украшает свой метод dispatch методом csrf_protect, где csrf_protect = decorator_from_middleware(CsrfViewMiddleware).

С вашим декоратором csrf_exempt на post и CsrfViewMiddleware из MIDDLEWARE, мы имеем что-то вроде CsrfViewMiddleware(csrf_protect(dispatch(csrf_exempt(post)))).

Мы можем обмануть csrf_protect, установив request.csrf_processing_done = True:

class ResetPasswordView(SuccessMessageMixin, PasswordResetView):
    ...

    @method_decorator(csrf_exempt)
    def dispatch(self, request, *args, **kwargs):
        request.csrf_processing_done = True
        return super().dispatch(request, *args, **kwargs)

    # @csrf_exempt  # Does nothing
    def post(self, request, *args, **kwargs):
        ...

Альтернативно, вы можете восстановить PasswordResetView.dispatch к неcsrf_protect функции:

PasswordResetView.dispatch = csrf_exempt(PasswordResetView.dispatch.__wrapped__)
Вернуться на верх