Flask Admin - 创建新用户时自动创建密码哈希?

Flask Admin - Automatically create password hash when creating a new user?

假设我有这样一个用户模型:

class User(db.Model, UserMixin):
    id = db.Column(db.Integer, primary_key=True)
    first_name = db.Column(db.String(255))
    last_name = db.Column(db.String(255))
    email = db.Column(db.String(255), unique=True)
    password = db.Column(db.String(255))
    registered_on = db.Column(db.DateTime, nullable=True)
    roles = db.relationship('Role', secondary=roles_users,
                            backref=db.backref('users', lazy='dynamic'))

以及管理员视图:

class UserView(MyModelView):

    form_columns = (
        'roles',
        'first_name',
        'last_name',
        'email',
        'password',
        'registered_on',
    )

    form_args = dict(
                registered_on=dict(default=datetime.now())
            )

当我创建一个新用户时,如何使用类似 bcrypt 的东西自动生成密码哈希?

您可以使用 Flask-Admin 的 on_model-change 并在提交到数据库之前为其提供逻辑。

from flask_admin.contrib import sqla
from flask_security import utils

class UserAdmin(sqla.ModelView):

    def on_model_change(self, form, model, is_created):
        model.password = utils.encrypt_password(model.password)
        # as another example
        if is_created:
            model.registered_on = datetime.now()  

我认为自动是指它应该自动将 manually-supplied plain-text 密码转换为散列,但我不确定。我的回答好像这个假设是正确的。

我们不会在 Flask-Admin 中公开我们的密码字段,但我们通过将 password 定义为带有 属性 来处理如何以及何时对密码进行哈希处理的基本问题=50=] 负责计算哈希本身并将其隐藏在 user._password.

尝试将 password 添加为管理视图中的列的天真尝试不起作用,但是如果您使用 sqlalchemy 的 hybrid_property 而不是 property,它看起来像这样在您的用户视图的 form_columns 列表中使用 "password" 效果很好(因为您已经拥有):

# models.py
from sqlalchemy.ext.hybrid import hybrid_property

class User(sql.Model):
    # ...

    _password = sql.Column(sql.Binary)
    _salt = sql.Column(sql.Binary, default=lambda: os.urandom(512))

    # ... 

    @hybrid_property
    def password(self):
        """Return the hashed user password."""
        return self._password

    @password.setter
    def password(self, new_pass):
        """Salt/Hash and save the user's new password."""
        new_password_hash = compute_new_password_hash(new_pass, self._salt)
        self._password = new_password_hash

# admin.py
class UserView(MyModelView):
    # ...

    form_columns = ("email", "password", ...)

编辑体验并不完美——您可能需要重写该字段以优化它。以下是一些截图:

正在使用 pre-set 密码创建新用户:

创建后的编辑视图:

更新用户密码:

保存新密码后的编辑视图:

我发现的最简单的选择是在 User.password 上添加一个 SQLAlchemy attribute event 并在密码更改时散列密码。这样,无论何时从任何地方更改用户密码,它都会自动散列。

from sqlalchemy import event
from werkzeug.security import generate_password_hash


@event.listens_for(User.password, 'set', retval=True)
def hash_user_password(target, value, oldvalue, initiator):
    if value != oldvalue:
        return generate_password_hash(value)
    return value

在内部,Flask-Admin 调用 WTFpopulate_obj 来设置表单中的数据。 所以我们可以利用它,如下所示:

class CustomPasswordField(PasswordField): # If you don't want hide the password you can use a StringField

    def populate_obj(self, obj, name):
        setattr(obj, name, hash_password(self.data)) # Password function

然后我们就可以在我们的ModelView中使用了,如下:

class UserView(ModelView):
    form_extra_fields = {
        'password': CustomPasswordField('Password', validators=[InputRequired()])
    }

密码将在保存表单时进行哈希处理。

您可以为 password 字段设置一个 PasswordField 小部件,并覆盖 process_formdata 方法来设置散列。


from flask_security.utils import hash_password
from wtforms import PasswordField

class AdminPasswordField(PasswordField):

    def process_formdata(self, valuelist):
        if valuelist and valuelist[0] != '':
            self.data = hash_password(valuelist[0])
        elif self.data is None:
            self.data = ''

class UserView(MyModelView):

    form_columns = (
        'roles',
        'first_name',
        'last_name',
        'email',
        'password',
        'registered_on',
    )

    form_args = dict(
                registered_on=dict(default=datetime.now())
            )

    form_overrides = {
        'password': AdminPasswordField,
    }