Использование passlib может зарегистрировать пароль, но не может проверить его снова, получая ошибку

#python #sqlite #flask #flask-sqlalchemy #passlib

#python #sqlite #flask #flask-sqlalchemy #passlib

Вопрос:

Я использую passlib для хранения пароля в моей базе данных sqlite. Я не получаю ошибку при сохранении нового пароля (регистрации). Но когда я пытаюсь войти в систему с тем же пользователем, я получаю эту ошибку ‘TypeError: хэш должен быть в юникоде или байтах, а не в sqlalchemy.orm.attributes.InstrumentedAttribute’.

Мой скрипт

 **models.py**
class Login(db.Model,UserMixin):
    "Creates username and password"
    id = db.Column(db.Integer,primary_key=True,nullable=False)
    username = db.Column(db.String,nullable=False)
    password = db.Column(db.String,nullable=False)
    email = db.Column(db.String,nullable=False)

    def __repr__(self):
        return f"Login('{self.id}','{self.username}','{self.password}','{self.email}')"

**routes.py**
from myfolder.security import encrypt_password,check_encrypted_password
def login():
   
    if request.method == 'POST':
        username = request.form.get('username')
        password1 = request.form.get('password')
        data = {'username':username}
        #fetch the email id of the user whose logged in
        user_email_id = Login.query.filter(Login.username==username).values(Login.email,Login.password)
        for logged_user in user_email_id:
            logged_email_id = logged_user.email
            hashed = logged_user.password
        session['logged_user'] = logged_email_id
        completion = validate(username)
        if completion ==False:
            error = 'error.'
        else:
            password_check = check_encrypted_password(password1,Login.password)
            if password_check ==False:
                error = 'error.'
            else:
                user = Login()
                user.name=username
                user.password=password
                login_user(user)
                error = 'Success'
        api_response = {'data':data,'error':error}
        return jsonify(api_response)
  

Я создал новый файл с именем security.py

 from passlib.context import CryptContext

pwd_context = CryptContext(
    schemes=["pbkdf2_sha256"],
    default="pbkdf2_sha256",
    pbkdf2_sha256__default_rounds=30000
)


def encrypt_password(password):
    return pwd_context.encrypt(password)


def check_encrypted_password(password, hashed):
    return pwd_context.verify(password, hashed)

  

В методе входа я пробовал разные способы, но ничего не работает.Я попытался передать пароль здесь.
password_check = check_encrypted_password(password1,password)
Но я получаю эту ошибку

 raise ValueError("hash could not be identified")
ValueError: hash could not be identified
  

Как я должен подтвердить свой пароль и логин?

Ответ №1:

В вашем коде ошибка. Строка

 password_check = check_encrypted_password(password1,Login.password)
  

должно быть

 password_check = check_encrypted_password(password1,hashed)
  

Вместо того, чтобы передавать хэшированный пароль из базы данных, вы в настоящее время передаете определение столбца sqlalchemy для пароля.

В вашем коде есть ряд других ошибок, о которых вы должны знать.

  1. Большую часть времени вы используете «password1», но есть также один экземпляр «password».
  2. Если указано имя пользователя, которого нет в базе данных, переменные «logged_email_id» и «hashed» не будут определены.

Итак, я бы предложил реорганизовать ваш код для этого:

 from myfolder.security import encrypt_password,check_encrypted_password
def login():    

    if request.method == 'POST':
        username = request.form.get('username')
        password = request.form.get('password')
        data = {'username':username}
        #fetch the email id of the user whose logged in
        logged_user = Login.query.filter(Login.username==username).values(Login.email,Login.password).one_or_none()
        if not logged_user:
            error = 'error.'
        else:
            session['logged_user'] = logged_user.email
            completion = validate(username)
            if completion ==False:
                error = 'error.'
            else:
                password_check = check_encrypted_password(password,logged_user.password)
                if password_check ==False:
                    error = 'error.'
                else:
                    user = Login()
                    user.name=username
                    user.password=password
                    login_user(user)
                    error = 'Success'
        api_response = {'data':data,'error':error}
        return jsonify(api_response)
  

В этом коде у меня есть:

  1. Заменил «password1» на «password», чтобы убедиться, что «user.password = пароль» не генерирует ошибку, поскольку пароль не определен.
  2. Заменил цикл for на «one_or_none ()». Это возвращает первое значение или нет, если имя пользователя не может быть найдено.
  3. Убедитесь, что значение, возвращаемое из запроса к базе данных, существует, прежде чем пытаться использовать результат запроса.

Комментарии:

1. Я попытался передать хэшированный пароль в качестве параметра « password_check = check_encrypted_password (password1, хэшированный)«но я получаю эту ошибку «password_check = check_encrypted_password (password1, хэшированный) Ошибка имени: имя ‘хэшированное’ не определено»

2. Итак, я обновил ответ, чтобы решить ошибку «хэш не определен» и ряд других проблем с вашим кодом.

3. Привет, спасибо, это сработало. Но этот запрос не сработал «»»logged_user = Login. запрос.фильтр (Login.username==имяпользователя).значения (Login.email, Login.password).one_or_none ()»»». Поэтому я удалил один или ни одного из запроса и использовал цикл for для получения значений из генератора.

4. Поскольку у вас есть рабочее решение, я просто добавлю это здесь для вашей информации. Таким образом, one_or_none выдаст исключение, когда в базе данных есть несколько строк, которые соответствуют запросу. Если запрос действительно возвращает несколько строк из базы данных, но вам все еще нужна только одна, тогда вы можете заменить «one_or_none ()» на «first()». Это вернет первый результат запроса или нет.