Можно ли сохранить токен в существующей таблице и использовать его для входа выхода из системы, изменения пароля, забывания пароля и истечения срока действия?
Необходимо использовать существующую таблицу для хранения токена, а также для хранения токена сброса пароля в той же таблице с истечением срока действия
Чтобы реализовать механизм безопасной смены пароля, предполагающий хранение токенов 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
Шаг 2: Обновление вида request_password_change
Измените представление 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')
Шаг 3: Обновление confirm_password_change
вида
Модифицируйте представление 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})
Шаг 4: Обновление шаблонов URL
В 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/<str:token>/', views.confirm_password_change, name='confirm_password_change'),
]
Шаг 5: Создание шаблонов для форм
request_password_change.html
<!DOCTYPE html>
<html>
<head>
<title>Request Password Change</title>
</head>
<body>
<h2>Request Password Change</h2>
<form method="POST">
{% csrf_token %}
<label for="email">Email:</label>
<input type="email" name="email" required><br><br>
<label for="current_password">Current Password:</label>
<input type="password" name="current_password" required><br><br>
<button type="submit">Send Password Change Link</button>
</form>
</body>
</html>
confirm_password_change.html
<!DOCTYPE html>
<html>
<head>
<title>Confirm Password Change</title>
</head>
<body>
<h2>Set a New Password</h2>
<form method="POST">
{% csrf_token %}
<label for="new_password">New Password:</label>
<input type="password" name="new_password" required><br><br>
<label for="confirm_password">Confirm Password:</label>
<input type="password" name="confirm_password" required><br><br>
<button type="submit">Change Password</button>
</form>
</body>
</html>
Шаг 6: Применение миграций
Выполните следующие команды, чтобы создать и применить миграции для новой модели:
python manage.py makemigrations
python manage.py migrate
Как это работает:
Вид:
request_password_change
- Проверяет текущий пароль пользователя.
- Генерирует JWT-токен.
- Сохраняет токен в
PasswordResetToken
модели. - Отправляет электронное письмо со ссылкой на токен пользователю.
Вид
confirm_password_change
:- Декодирует и проверяет токен JWT.
- Проверяет, существует ли токен в базе данных и не истек ли срок его действия.
- Позволяет пользователю установить новый пароль.
- Отмечает токен как просроченный после успешной смены пароля.
Дополнительные соображения:
- Убедитесь, что
SECRET_KEY
находится в безопасности. - Используйте HTTPS в производстве для защиты токенов и конфиденциальных данных.
- Рассмотрите возможность добавления фоновой задачи для периодической очистки просроченных токенов.
Эта реализация обеспечивает безопасное хранение токенов в базе данных