使用bcrypt密码哈希进行用户身份验证

时间:2016-12-22 12:48:02

标签: python flask bcrypt

我正在尝试在我的应用中使用散列密码,如下所示:

class UserService():

    def register_user(self, username, email, password):
        if self.__checkIfUserExists(username) is True:
            return False
        else:

            hashed_password = self.__hash_password(password.encode('utf-8'), bcrypt.gensalt())

            user = User(username=username, email=email, password_hash=hashed_password)

            db.session.add(user)
            db.session.commit()
            db.session.close()

        return True

    def authenticate_user(self, email, password):
        user = User.query.filter_by(email=email).first()
        hashed_pw = self.__hash_password(password.encode("utf-8"), bcrypt.gensalt())
        print(hashed_pw == user.password_hash)
        if user and hashed_pw == user.password_hash:
            return True
        return False


    def __checkIfUserExists(self, username):
        exists = db.session.query(db.exists().where(User.username== username)).scalar()
        return exists

    def __hash_password(self, password, salt):
        return bcrypt.hashpw(password, salt)

嗯,密码永远不会匹配。

我如何让它工作?我的错误在哪里?我以为我必须将提供的密码的哈希值与数据库中存储的哈希值进行比较..?

3 个答案:

答案 0 :(得分:3)

来自https://pypi.python.org/pypi/bcrypt/2.0.0

>>> import bcrypt
>>> password = b"super secret password"
>>> # Hash a password for the first time, with a randomly-generated salt
>>> hashed = bcrypt.hashpw(password, bcrypt.gensalt())
>>> # Check that a unhashed password matches one that has previously been
>>> #   hashed
>>> if bcrypt.hashpw(password, hashed) == hashed:
...     print("It Matches!")
... else:
...     print("It Does not Match :(")

请注意hashpw接受盐或以前的哈希密码(包括用于创建它的盐),以便进行适当的比较。

您已使用bcrypt.gensalt()两次,这会产生两种不同的随机盐,因此散列会有所不同。

答案 1 :(得分:0)

亚历克斯是对的,第二次加入gensalt是个问题。但是你不必再次计算哈希值。

从版本3.1.0开始,已添加checkpw。

要保持pythonic,您可以使用checkpw而不是再次与哈希密码进行比较。

>>> import bcrypt
>>> password= 'cantguessme'
>>>hashed= bcrypt.hashpw(password.encode('utf-8'), bcrypt.gensalt(12))
>>>if bcrypt.checkpw(password, hashed):
   ...     print("Yaay, It Matches!")
   ... else:
   ...     print("Oops, It Does not Match :(")

答案 2 :(得分:0)

此答案可能对使用 DB 保存加密密码的人有所帮助。

问题:我将编码后的密码保存到字符串列中。

解决方案:散列密码是二进制类型,必须保存为二进制,而不是字符串或 JSON。

我的代码中密码列的列属性,我在这里使用了 Postgresql、Flask 和 sqlalchemy,它们在我更改列数据类型后起作用

from sqlalchemy import Column, String, Integer, Boolean, Date, LargeBinary
from db import Base

class User(Base):
    __tablename__ = 'users'

    id = Column(Integer, primary_key=True, autoincrement=True)
    userId = Column(String(50), nullable=False)
    firstName = Column(String(50), nullable=False)
    password = Column(LargeBinary(100), nullable=False)
    recovery = Column(String(25))
    phone = Column(String(10), nullable=False, unique=True)
    phoneVerified = Column(Boolean, nullable=False)
    email = Column(String(50), nullable=False, unique=True)
    emailVerified = Column(Boolean, nullable=False)

    def __init__(self, userId, firstName, password, phone, phoneVerified, email, emailVerified, recovery):
        self.userId = userId
        self.firstName = firstName
        self.password = password
        self.phone = phone
        self.email = email
        self.phoneVerified = phoneVerified
        self.emailVerified = emailVerified
        self.recovery = recovery

资源代码:

import bcrypt
import shortuuid
from flask_restful import Resource, reqparse
from entities.User import User

class Register(Resource):
    @staticmethod
    def post():
        data = Register.parser.parse_args()
        hashed = bcrypt.hashpw(data.password.encode('utf-8'), bcrypt.gensalt())

        user = User(userId=shortuuid.ShortUUID().random(length=10), firstName=data.firstName,
                                    password=hashed, phone=data.phone, phoneVerified=False, email=data.email,
                                    emailVerified=False, recovery=shortuuid.ShortUUID().random(length=25))
        session.add(user)
        session.commit()
        session.close()
        return {'description': 'You are registered'}, 201
        

class Login(Resource):
    @staticmethod
    def post():
        data = Login.parser.parse_args()
        user = find_by_email(data.email)
        if user:
            pw_check = bcrypt.checkpw(password=data.password.encode('utf-8'), hashed_password=user.password)
            access_token = create_access_token(identity=user.id, fresh=True)
            refresh_token = create_refresh_token(identity=user.id)
            return {'access_token': access_token, 'refresh_token': refresh_token}, 200
        return {'description': 'Invalid credentials'}, 401