Flask-SqlAlchemy、Bcrypt、Postgres 编码问题

Flask-SqlAlchemy, Bcrypt, Postgres issue with encoding

我正在从头开始编写我的第一个 API,并且有一个 /login 端点在使用 bcrypt 验证用户密码时出错,但仅当使用 Postgres 作为我的数据库时,在使用 SQLite3 时才能正常工作。

此外,我们始终欢迎任何有关更好地构建我的模型或路线的方法的帮助,这是我在 Flask 中的第一个 API / Python 所以我仍在学习。

提前致谢!

错误:

* Running on http://127.0.0.1:5000/ (Press CTRL+C to quit)
[2021-06-22 12:06:14,415] ERROR in app: Exception on /api/v1/login [POST]
Traceback (most recent call last):
  File "C:\Users\x4c8\Projects\money_api\venv\lib\site-packages\flask\app.py", line 2070, in wsgi_app
response = self.full_dispatch_request()
File "C:\Users\x4c8\Projects\money_api\venv\lib\site-packages\flask\app.py", line 1515, in full_dispatch_request
rv = self.handle_user_exception(e)
File "C:\Users\x4c8\Projects\money_api\venv\lib\site-packages\flask\app.py", line 1513, in full_dispatch_request
rv = self.dispatch_request()
File "C:\Users\x4c8\Projects\money_api\venv\lib\site-packages\flask\app.py", line 1499, in dispatch_request
return self.ensure_sync(self.view_functions[rule.endpoint])(**req.view_args)
File "C:\Users\x4c8\Projects\money_api\routes.py", line 47, in token_get
check = user.verify_password(password)
File "C:\Users\x4c8\Projects\money_api\models.py", line 40, in verify_password
return bcrypt.checkpw(enc_pw, self.password_hash)
File "C:\Users\x4c8\Projects\money_api\venv\lib\site-packages\bcrypt\__init__.py", line 120, in checkpw
raise TypeError("Unicode-objects must be encoded before checking")
TypeError: Unicode-objects must be encoded before checking
127.0.0.1 - - [22/Jun/2021 12:06:14] "POST /api/v1/login HTTP/1.1" 500 -

Models.py 中的用户 class:

class User(db.Model, Serializer):
__tablename__ = 'user'
id = db.Column(db.Integer, primary_key=True)
first_name = db.Column(db.String(15), unique=False, nullable=True)
last_name = db.Column(db.String(20), unique=False, nullable=True)
email = db.Column(db.String(120), unique=True, nullable=False)
password_hash = db.Column(db.String(255), unique=False, nullable=False)
country = db.Column(db.String(2), unique=False, nullable=True)
subscription_level = db.Column(db.Integer, default=0)
subscription_purchase_date = db.Column(db.DateTime(), unique=False, nullable=True)
last_login = db.Column(db.DateTime(), unique=False, default=datetime.utcnow)
modified_at = db.Column(db.DateTime(), unique=False, default=datetime.utcnow)
created_at = db.Column(db.DateTime(), unique=False, default=datetime.utcnow)

# relationships
portfolios = db.relationship('StockPortfolio', foreign_keys='StockPortfolio.fk_user', backref='user',
                             lazy='dynamic', cascade='all, delete-orphan')

@property
def password(self):
    raise AttributeError('password not readable')

@password.setter
def password(self, password):
    enc_pw = password.encode('utf-8')
    self.password_hash = bcrypt.hashpw(enc_pw, bcrypt.gensalt()).decode('utf-8')

def verify_password(self, password):
    enc_pw = password.encode('utf-8')
    return bcrypt.checkpw(enc_pw, self.password_hash)

def serialize(self):
    d = Serializer.serialize(self)
    del d['password_hash']
    del d['modified_at']
    del d['created_at']
    del d['last_login']
    return d

/从 routes.py

登录
# POST /login
@routes.route(api_v1 + 'login', methods=['POST'])
def token_get():
    if request.method == 'POST':
        body = request.get_json()

        # fail on missing params
        if body.get('email') is None:
            return jsonify(msg='email parameter is missing'), 422
        if body.get('password') is None:
            return jsonify(msg='password parameter is missing'), 422

        # fail on email not in use
        user = User.query.filter_by(email=body.get('email')).first()
        if user is None:
            return jsonify(msg='Email is not in use'), 404
        else:
            password = body.get('password')
            check = user.verify_password(password)

            if check:
                # record last login
                user.last_login = datetime.utcnow()

                # prep and return tokens
                access_token = create_access_token(identity=user.id)
                refresh_token = create_refresh_token(identity=user.id)
                return jsonify(msg='login successful', access_token=access_token, refresh_token=refresh_token), 200
            else:
                return jsonify(msg='incorrect email or password'), 409

您只需更改这部分代码即可将 password_hash 转换为字节:

def verify_password(self, password):
    enc_pw = password.encode('utf-8')
    return bcrypt.checkpw(enc_pw, bytes(self.password_hash, 'utf-8'))