Чтобы реализовать механизм безопасной смены пароля, который предполагает хранение токенов JWT в базе данных для проверки, вам необходимо изменить предыдущее решение, чтобы сохранять и проверять токены из базы данных.
Вот пошаговое руководство по реализации этого:
Шаг 1. Создайте модель для хранения токенов JWT
- Создайте новую модель в своем приложении Django для хранения JWT жетоны. Эта модель будет включать поля для токена, пользователя, с которым он связан, и статуса его срока действия.
Код: Выделить всё
# models.py in your Django app
from django.db import models
from django.contrib.auth.models import User
from django.utils import timezone
class PasswordResetToken(models.Model):
user = models.ForeignKey(User, on_delete=models.CASCADE, related_name='reset_tokens')
token = models.CharField(max_length=255, unique=True)
is_expired = models.BooleanField(default=False)
created_at = models.DateTimeField(auto_now_add=True)
def __str__(self):
return f'Token for {self.user.username}'
def has_expired(self):
# Check if the token has expired (assuming 30 minutes expiry time)
expiry_time = self.created_at + timezone.timedelta(minutes=30)
return timezone.now() > expiry_time
Измените представление request_password_change, чтобы сгенерировать токен JWT, сохраните его в базу данных и отправьте ее по электронной почте.
Код: Выделить всё
# views.py in your Django app
import jwt
from django.contrib.auth.models import User
from django.contrib.auth.hashers import check_password, make_password
from django.core.mail import send_mail
from django.conf import settings
from django.http import JsonResponse, HttpResponse
from django.shortcuts import render, redirect
from django.utils import timezone
from .models import PasswordResetToken
import datetime
def request_password_change(request):
"""
View to request a password change. It verifies the current password,
generates a JWT token, saves it in the database, and sends it to the user's email.
"""
if request.method == 'POST':
email = request.POST.get('email')
current_password = request.POST.get('current_password')
try:
user = User.objects.get(email=email)
except User.DoesNotExist:
return JsonResponse({'error': 'User with this email does not exist.'}, status=404)
if not check_password(current_password, user.password):
return JsonResponse({'error': 'Current password is incorrect.'}, status=400)
# Generate JWT token
payload = {
'user_id': user.id,
'exp': datetime.datetime.utcnow() + datetime.timedelta(minutes=30)
}
token = jwt.encode(payload, settings.SECRET_KEY, algorithm='HS256')
# Save the token in the database
PasswordResetToken.objects.create(user=user, token=token)
# Construct the reset URL and send email
reset_url = request.build_absolute_uri(f"/confirm-password-change/{token}/")
send_mail(
subject="Password Change Request",
message=f"Click the following link to change your password:\n{reset_url}",
from_email=settings.EMAIL_HOST_USER,
recipient_list=[user.email],
fail_silently=False,
)
return JsonResponse({'message': 'Password change link sent to your email.'}, status=200)
return render(request, 'request_password_change.html')
Измените представление «Confirm_password_change», чтобы проверить токен из базы данных и разрешить пользователю изменить свой пароль.
Код: Выделить всё
# views.py in your Django app
import jwt
from django.contrib.auth.models import User
from django.contrib.auth.hashers import make_password
from django.http import HttpResponse, JsonResponse
from django.shortcuts import render
from django.utils import timezone
from .models import PasswordResetToken
import datetime
def confirm_password_change(request, token):
"""
View to confirm password change using the provided JWT token.
Verifies the token against the database and allows the user to set a new password.
"""
try:
# Decode the JWT token
payload = jwt.decode(token, settings.SECRET_KEY, algorithms=['HS256'])
user_id = payload.get('user_id')
except jwt.ExpiredSignatureError:
return HttpResponse("The token has expired.", status=400)
except jwt.InvalidTokenError:
return HttpResponse("Invalid token.", status=400)
# Check if the token exists in the database and is not expired
try:
reset_token = PasswordResetToken.objects.get(token=token, user_id=user_id)
except PasswordResetToken.DoesNotExist:
return HttpResponse("Invalid or used token.", status=400)
# Check if the token has expired
if reset_token.has_expired() or reset_token.is_expired:
return HttpResponse("The token has expired or is already used.", status=400)
if request.method == 'POST':
new_password = request.POST.get('new_password')
confirm_password = request.POST.get('confirm_password')
if new_password != confirm_password:
return JsonResponse({'error': 'Passwords do not match.'}, status=400)
# Update user's password
user = User.objects.get(id=user_id)
user.password = make_password(new_password)
user.save()
# Mark the token as expired
reset_token.is_expired = True
reset_token.save()
return JsonResponse({'message': 'Password changed successfully.'}, status=200)
return render(request, 'confirm_password_change.html', {'token': token})
В urls.py вашего приложения добавьте следующие URL-адреса:
Код: Выделить всё
# urls.py in your Django app
from django.urls import path
from . import views
urlpatterns = [
path('request-password-change/', views.request_password_change, name='request_password_change'),
path('confirm-password-change//', views.confirm_password_change, name='confirm_password_change'),
]
- [/b]
Код: Выделить всё
request_password_change.html
Код: Выделить всё
Request Password Change
Request Password Change
{% csrf_token %}
Email:
Current Password:
Send Password Change Link
- [/b]
Код: Выделить всё
confirm_password_change.html
Код: Выделить всё
Confirm Password Change
Set a New Password
{% csrf_token %}
New Password:
Confirm Password:
Change Password
Выполните следующие команды, чтобы создать и применить миграции для новой модели:
< pre class="lang-bash Prettyprint-override">
Код: Выделить всё
python manage.py makemigrations
python manage.py migrate
- Представление request_password_change:
- Проверяет текущий пароль пользователя.
- Создает токен JWT.
- Сохраняет токен в Модель PasswordResetToken.
- Отправляет пользователю электронное письмо со ссылкой на токен.
- Представление submit_password_change:
- Декодирует и проверяет токен JWT.
- Проверяет, существует ли токен в базе данных и не истек ли срок его действия.
- Разрешает пользователю установить новый пароль.
- Отмечает токен срок его действия истек после успешной смены пароля.
- Гарантируйте безопасность SECRET_KEY.
- Используйте HTTPS в рабочей среде для защиты токенов и конфиденциальных данных.
- Рассмотрите возможность добавления фоновой задачи для периодической очистки токенов с истекшим сроком действия.
Эта реализация гарантирует безопасное хранение токенов в базе данных
п>
Подробнее здесь: https://stackoverflow.com/questions/790 ... t-for-logi