Ваша ошибка вызвана определением свойства password
:
@property
def password(self):
raise AttributeError('password is not a readable attribute')
Это заменило предыдущее определение для имени password
объектом свойства без установщика.Вы можете воспроизвести проблему с помощью
>>> class Foo:
... @property
... def bar(self):
... raise AttributeError('bar is not a readable attribute')
...
>>> f = Foo()
>>> f.bar = 'new value'
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
AttributeError: can't set attribute
Свойства не имеют установщиков, если вы не указали их явно, а свойства - это просто больше объектов в пространстве имен класса, вы не можете использовать одно и то же имя для определения столбцаи свойство.
Вы можете просто переименовать столбец, например, в _password
, но здесь есть большая проблема: Вы никогда не должны хранить пароли пользователей в своей базе данных .
Вместо этого вы должны хранить пароль хэшей , сгенерированные криптографически уникальные строки, которые могут быть воспроизведены только в том случае, если в будущем ваша программа будет представлена с тем же паролем.Таким образом, вы можете проверить , что кто-то все еще знает правильный пароль, не давая хакерам доступа к паролям, если им удастся украсть вашу базу данных.
Очень важно, чтобывы никогда не предоставляете хакерам доступ к паролям .См. этот анализ нарушения безопасности в Adobe , чтобы узнать, почему вы никогда не захотите оказаться в такой же ситуации.Моя учетная запись была взломана и в этом хакере, но только потому, что я использую уникальные пароли для каждого сайта, хакеры не смогли повторно использовать информацию моей учетной записи для доступа к другим сайтам.
См. это руководство для хорошего обзора того, что вы должны делать вместо этого.Flask поставляется с необходимыми инструментами, чтобы сделать это для вас.
Здесь должно быть достаточно использовать пакет werkzeug.security
:
from sqlalchemy.ext.hybrid import hybrid_property
from werkzeug.security import generate_password_hash, check_password_hash
class Credentials(db.Model):
id = db.Column(db.Integer, primary_key=True)
fname = db.Column(db.String(32), nullable=False)
lname = db.Column(db.String(32))
username = db.Column(db.String(16), unique=True, nullable=False)
email = db.Column(db.String(64), unique=True, nullable=False)
date_created = db.Column(db.DateTime, default=datetime.utcnow)
_password_hash = db.Column('password', db.String(100))
@hybrid_property
def password(self):
return self._password_hash
@password.setter
def _set_password(self, plaintext):
self._password_hash = generate_password_hash(plaintext)
def validate_password(self, password):
return check_password_hash(self._password_hash, password)
def __repr__(self):
return f"Users('{self.fname}', '{self.email}', '{self.username}')"
Я использовал Объект гибридного свойства SQLAlchemy для обработки атрибута password
здесь.Обратите внимание, что столбец хранится в _password
(но столбец базы данных по-прежнему называется password
) и что значение автоматически хешируется при его установке.
Затем можнопроверять пароли по хешу, сохраненному в базе данных, с помощью метода check_password()
.
Реализация werkzeug.security
защищает от временных атак (где злоумышленник может сработать, если он близок к правильному паролю, измеряя, как долго ониспользует ваш сервер для проверки паролей), но слабее в том, что поддерживает устаревшие протоколы.Другие пакеты, такие как Flask-BCrypt, позволяют настраивать более строгие политики.