SQLAlchemy 将密码转换为十六进制值

SQLAlchemy converts Password to HEX Value

我在我的一个项目中使用 Sanic + SQLAlchemy。
基本上,我有这个用于注册用户的端点

from sanic import Blueprint, response
from sanic.request import Request
from sanic.response import HTTPResponse
import bcrypt
import logging
from sanic_cors import CORS, cross_origin
from services.userservice import create_user, find_by_username


@bp.route("/add",methods=["POST", "OPTIONS"])
@cross_origin(bp, origins='*',automatic_options=True)
async def register(req: Request) -> HTTPResponse:
    """
    Register the user in the system
    :param req: the request sent from the user. Will contain the necessary parameters to save the user in the system
    :return 200 if the user is registered, 500 if an error happens
    """
    LOGGER.info(">>>>>> /user/add")
    chosen_username = req.json['utente']['username']
    chosen_pwd = req.json['utente']['password']
    salt = bcrypt.gensalt(rounds = 10, prefix=b"2a")
    hashed = bcrypt.hashpw(chosen_pwd.encode('utf-8'), salt)
    insert_success = await create_user(chosen_username,hashed,req.app.session_pgsql)
    if insert_success:
        LOGGER.info(f"[{chosen_username}] successfully created!")
        created_user = await find_by_username(chosen_username, req.app.session_pgsql)
        return response.json({'status': 200, 'message': 'OK', 'data': created_user}, 200)
    else:
        LOGGER.error(f"Couldn't add {chosen_username} to the database!")
        return response.json({'status':500, 'message': 'ERROR', 'data': None},500)

bcrypt 以这种方式实例化盐,因为身份验证过程由 Java 微服务执行。
然后,当代码到达create_user方法时

async def create_user(chosen_username: str, hashed: str, session_pgsql: Session) -> bool:
    """
    Creates the specified user in the database
    :param chosen_username: the chosen username
    :param hashed: the hashed password that the new user chose
    :param session_pgsql: the SQLAlchemy Session Object that will insert the user in the DB
    :return True if the insert was successful, False otherwhise
    """
    new_user = Utente(username=chosen_username, pwd=hashed)
    session_pgsql.add(new_user)
    try:
        session_pgsql.commit()
    except Exception as e:
        session_pgsql.rollback()
        return False
    return True

保存操作进行得很顺利,但是在我的 PostgreSQL 数据库中(使用 postgresql+psycopg2 驱动程序 URL 配置建立连接),我看到密码已保存作为十六进制字符序列。 例如,即使我尝试

session_pgsql.query(Utente).filter(Utente.id == new_user.id).update({Utente.pwd : hashed}, synchronize_session = False)

new_user.pwd = hashed
session_pgsql.commit()

在 PyCharm 的调试模式下,密码仍然显示为十六进制字符序列。 有人可能对发生的事情有一个模糊的线索吗?据我在调试中看到的,密码正确到达 create_user 方法,所以我认为问题必须依赖于 SQLAlchemy 内部
Utente 模型是这样的class

from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy import Column, Integer, String, Boolean
from sqlalchemy.orm import relationship

Base = declarative_base()

"""
The ORM class that represents the 'Utente' table 
"""
class Utente(Base):
   __tablename__ = 'utente'
   id = Column(Integer, primary_key=True)

   username = Column(String, unique=True)
   pwd = Column(String)
   enabled = Column(Boolean, default=False)
   first_access_done = Column(Boolean, default=False)

编辑:根据要求,这是我的数据发生的情况。
在提交数据库之前,Utente 对象看起来像这样。

new_user.username
Out[4]: 'acooltestmail@gmail.com'
In[5]: 
new_user.pwd
Out[5]: b'a$WhF2CBCCm1MLaoOlwRx4YeA.uMQNtL3XHOI6i09ZbWkKDp5hxA2Fe'

然后,这个查询由SQLAlchemy执行

2020-06-05 20:27:02,973 INFO sqlalchemy.engine.base.Engine BEGIN (implicit)
2020-06-05 20:27:03,016 INFO sqlalchemy.engine.base.Engine INSERT INTO utente (username, pwd, enabled, first_access_done) VALUES (%(username)s, %(pwd)s, %(enabled)s, %(first_access_done)s) RETURNING utente.id
2020-06-05 20:27:03,016 INFO sqlalchemy.engine.base.Engine {'username': 'acooltestmail@gmail.com', 'pwd': b'a$WhF2CBCCm1MLaoOlwRx4YeA.uMQNtL3XHOI6i09ZbWkKDp5hxA2Fe', 'enabled': False, 'first_access_done': False}
2020-06-05 20:27:03,037 INFO sqlalchemy.engine.base.Engine COMMIT

但是如果我去数据库中查看结果查询,结果实际上是这样的

id|username              |pwd                                                                                                                       |enabled|first_access_done|
--|----------------------|--------------------------------------------------------------------------------------------------------------------------|-------|-----------------|
 5|acooltestmail@gmail.com|\x2432612431302457684632434243436d314d4c616f4f6c775278345965412e754d514e744c3358484f49366930395a62576b4b447035687841324665|false  |false            |

最后恰好是我这边的失误。
当我看到 bcrypt 的哈希结果在调试器中呈现为 b'$2a$10$WhF2CBCCm1MLaoOlwRx4YeA.uMQNtL3XHOI6i09ZbWkKDp5hxA2Fe' 时,我产生了疑问。
我相信 bcrypt 的 hashpw 方法返回一个 string,而它实际上 returns 一个 bytes 对象。因此,如果保存到 PostgreSQL varchar 字段,数据库会尝试将该值转换为实际字符串(就像 Ilja Everilä 在评论中建议的那样)。