Может ли кто-нибудь показать мне, как сделать правильную систему входа для моего django приложения?
Мне нужна помощь в создании правильной системы входа в систему для моего домашнего задания в классе django. Я должен создать приложение, в которое пользователи могут войти с помощью аутентификации на основе токенов, а также могут размещать информацию о месте назначения и просматривать ее, но я застрял в разделе входа. Мне удалось правильно построить систему регистрации, но теперь мне нужна помощь с входом в систему.
Это мой вид входа в систему, который я имею. Он постоянно выдает ошибку «NOT NULL constraint failed:», поэтому я понятия не имею, что я делаю не так
def login(request: HttpRequest):
if request.method == "POST":
email = request.POST.get('email')
password_hash = request.POST.get('password')
hasher = hashlib.sha256()
hasher.update(bytes(password_hash, "UTF-8"))
password_hash = hasher.hexdigest()
token = ""
letters = string.ascii_lowercase
for _ in range(32):
token = "".join(random.choice(letters) for _ in range(32))
session_token = Session.objects.create(token=token)
response = HttpResponse("Cookie Set")
try:
user = User.objects.get(email=email, password_hash=password_hash)
if user.check_password(password_hash):
response = redirect("/")
response.set_cookie('token', token)
return response
else:
return HttpResponse("Invalid password")
except User.DoesNotExist:
return Http404({"Invalid email"})
return render(request, "destination/login.html")
Вот мои модели
from django.db import models
import hashlib
class User(models.Model):
id = models.BigAutoField(primary_key=True)
name = models.TextField()
email = models.EmailField(unique=True)
password_hash = models.TextField()
def check_password(self, hashed_password):
hasher = hashlib.sha256()
hasher.update(bytes(self.password_hash, "UTF-8"))
hashed_password = hasher.hexdigest()
return self.password_hash == hashed_password
class Session(models.Model):
id = models.BigAutoField(primary_key=True)
user = models.OneToOneField(User, on_delete=models.CASCADE)
token = models.TextField()
class Destination(models.Model):
id = models.BigAutoField(primary_key=True)
name = models.TextField()
review = models.TextField()
rating = models.PositiveIntegerField()
share_publicly = models.BooleanField(default=False)
user = models.ForeignKey("User", related_name="destinations", on_delete=models.CASCADE)
А вот моя форма входа в систему:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<form action="/login" method="POST">
<label for="">Email</label>
<input type="email" name="email" id="email">
<label for="">Password</label>
<input type="password" name="password" id="password">
<button type="submit">Login</button>
</form>
</body>
</html>
Если кто-то может понять, что не так, или помочь мне найти лучший способ, я буду благодарен. Также я не имею права использовать встроенные модули Auth, Session или Admin, так что, пожалуйста, не предлагайте. Бонус, если вы покажете мне, как показывать только направления от вошедшего пользователя xD
Перед созданием сеанса необходимо убедиться, что у вас есть объект пользователя.
Кроме того, необходимо правильно проверить пароль, не повторяя его без необходимости.
Я также вижу, что вы используете имя поля password
в форме, но в представлении вы пытаетесь получить его с помощью password_hash
. Это приведет к получению пустого хэша пароля, поэтому вход в систему будет неудачным.
Вы можете обновить свой вид логина следующим образом:
import hashlib
import random
import string
from django.shortcuts import render, redirect
from django.http import HttpResponse
from .models import User, Session
def login(request):
if request.method == "POST":
email = request.POST.get('email')
password = request.POST.get('password')
user = User.objects.filter(email=email).first()
if user and user.check_password(password):
token = ''.join(random.choice(string.ascii_letters) for _ in range(32))
session = Session.objects.create(user=user, token=token)
response = redirect("/")
response.set_cookie('token', token)
return response
return HttpResponse("Invalid email or password")
return render(request, "destination/login.html")
И обновление check_password
метода в моделях:
class User(models.Model):
# Fields...
def check_password(self, password):
hasher = hashlib.sha256()
hasher.update(bytes(password, "UTF-8"))
return self.password_hash == hasher.hexdigest()
И наконец, чтобы отобразить только направления от вошедшего пользователя, вам нужно получить пользователя из токена, хранящегося в cookie. Вот представление для отображения пунктов назначения:
def user_destinations(request):
token = request.COOKIES.get('token')
if token:
session = Session.objects.filter(token=token).first()
if session:
destinations = session.user.destinations.all()
return render(request, "destination/user_destinations.html", {"destinations": destinations})
return redirect("/login")
Ну, proper
логин - это относительно. Сам фреймворк уже имеет систему proper
логина из коробки, хотя и session
. Существует множество различных видов систем аутентификации. Тем не менее, судя по вашему вопросу, вы спрашиваете об аутентификации с помощью Tokens
, которая часто используется в API-сервисах. Существует два стандарта: обычная Token Authentication
или JWT - JSON Web Token Authentication
Обычно разработчик не тратит время на такие вещи. Для этого и существуют библиотеки. Всегда лучше полагаться на такие пакеты, чем реализовывать их самостоятельно, не только для гибкости, но и для безопасности.
Относительно User
модели, вы работаете против фреймворка, тем самым мешая себе. Если вы хотите создать пользовательскую модель, то вы должны сделать это правильно. Не используя модель, предоставляемую фреймворком, вы упускаете множество встроенных функций, таких как хеширование паролей.
С учетом сказанного, вот пример неглубокой аутентификации с помощью токенов:
models.py
class Token(models.Model):
key = models.UUIDField(default=uuid.uuid4)
user = models.OneToOneField(User, on_delete=models.CASCADE)
expiration = models.DateTimeField()
@property
def is_expired(self):
return True if timezone.now() > self.expiration else False
def renew(self):
"""
Renew a token key and makes it valid for 3hours
its common for a obj to have a refresh_key in order
to do this process, which would require more logic.
"""
self.key = uuid.uuid4()
self.expiration = timezone.now() + datetime.timedelta(hours=3)
self.save()
def check_key(self, value):
try:
key = uuid.UUID(value)
assert key == self.key
return True
except Exception:
return False
views.py
def login(request):
if request.method == "POST":
email = request.POST.get("email")
password = request.POST.get("password")
token_key = request.COOKIES.get("token")
try:
# Fetch user and check password
user = User.objects.get(email=email)
if user.check_password(password):
# Verify token validity, renew if necessary
token = Token.objects.get(user=user)
if token.check_key(token_key):
if token.is_expired:
token.renew()
response = HttpResponse("Authentication Successfull")
response.set_cookie("token", token.key)
return response
else:
return HttpResponse("Invalid Token")
else:
return HttpResponse("Wrong Password")
except ObjectDoesNotExist:
return HttpResponse("User Not Found.")
else:
return render(request, "login.html")