Можно ли сохранить токен в существующей таблице и использовать его для входа выхода из системы, изменения пароля, забывания пароля и истечения срока действия?

Необходимо использовать существующую таблицу для хранения токена, а также для хранения токена сброса пароля в той же таблице с истечением срока действия

Чтобы реализовать механизм безопасной смены пароля, предполагающий хранение токенов JWT в базе данных для проверки, необходимо модифицировать предыдущее решение для сохранения и проверки токенов из базы данных.

Вот пошаговое руководство по реализации этого:

Шаг 1: Создание модели для хранения JWT-токенов

  1. Создайте новую модель в вашем приложении 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: Создание шаблонов для форм

  1. 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>
  1. 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

Как это работает:

  1. Вид: request_password_change

    • Проверяет текущий пароль пользователя.
    • Генерирует JWT-токен.
    • Сохраняет токен в PasswordResetToken модели.
    • Отправляет электронное письмо со ссылкой на токен пользователю.
  2. Вид confirm_password_change:

    • Декодирует и проверяет токен JWT.
    • Проверяет, существует ли токен в базе данных и не истек ли срок его действия.
    • Позволяет пользователю установить новый пароль.
    • Отмечает токен как просроченный после успешной смены пароля.

Дополнительные соображения:

  • Убедитесь, что SECRET_KEY находится в безопасности.
  • Используйте HTTPS в производстве для защиты токенов и конфиденциальных данных.
  • Рассмотрите возможность добавления фоновой задачи для периодической очистки просроченных токенов.

Эта реализация обеспечивает безопасное хранение токенов в базе данных

Вернуться на верх