Как отключить проверку 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__)