gpt4 book ai didi

python - 使用 php 格式的函数 password_hash() 的 Django 密码哈希器

转载 作者:行者123 更新时间:2023-12-04 13:00:45 24 4
gpt4 key购买 nike

我必须添加一个向后兼容的 Django 应用程序,该应用程序支持保留在使用 PHP 函数创建的数据库中的旧密码 password_hash()哪个输出像

$2y$10$puZfZbp0UGMYeUiyZjdfB.4RN9frEMy8ENpih9.jOEngy1FJWUAHy

(具有 10 轮散列的咸河豚加密算法)

Django 支持带有算法前缀名称的格式,所以如果我使用 BCryptPasswordHasher因为主哈希器输出将如下所示:
bcrypt$$2y$10$puZfZbp0UGMYeUiyZjdfB.4RN9frEMy8ENpih9.jOEngy1FJWUAHy

我创建了自定义 BCryptPasswordHasher,如:
class BCryptPasswordHasher(BasePasswordHasher):
algorithm = "bcrypt_php"
library = ("bcrypt", "bcrypt")
rounds = 10

def salt(self):
bcrypt = self._load_library()
return bcrypt.gensalt(self.rounds)

def encode(self, password, salt):
bcrypt = self._load_library()
password = password.encode()
data = bcrypt.hashpw(password, salt)
return f"{data.decode('ascii')}"

def verify(self, incoming_password, encoded_db_password):
algorithm, data = encoded_db_password.split('$', 1)
assert algorithm == self.algorithm

db_password_salt = data.encode('ascii')
encoded_incoming_password = self.encode(incoming_password, db_password_salt)
# Compare of `data` should only be done because in database we don't persist alg prefix like `bcrypt$`
return constant_time_compare(data, encoded_incoming_password)

def safe_summary(self, encoded):
empty, algostr, work_factor, data = encoded.split('$', 3)
salt, checksum = data[:22], data[22:]
return OrderedDict([
('algorithm', self.algorithm),
('work factor', work_factor),
('salt', mask_hash(salt)),
('checksum', mask_hash(checksum)),
])

def must_update(self, encoded):
return False

def harden_runtime(self, password, encoded):
data = encoded.split('$')
salt = data[:29] # Length of the salt in bcrypt.
rounds = data.split('$')[2]
# work factor is logarithmic, adding one doubles the load.
diff = 2 ** (self.rounds - int(rounds)) - 1
while diff > 0:
self.encode(password, salt.encode('ascii'))
diff -= 1

而 AUTH_USER_MODEL 就像:
from django.contrib.auth.hashers import check_password
from django.db import models


class User(models.Model):
id = models.BigAutoField(primary_key=True)
email = models.EmailField(unique=True)
password = models.CharField(max_length=120, blank=True, null=True)
USERNAME_FIELD = 'email'
REQUIRED_FIELDS = []
EMAIL_FIELD = 'email'

def check_password(self, raw_password):
def setter():
pass

alg_prefix = "bcrypt_php$"
password_with_alg_prefix = alg_prefix + self.password
return check_password(raw_password, password_with_alg_prefix, setter)

设置 base.py :
...
AUTH_USER_MODEL = 'custom.User'

PASSWORD_HASHERS = [
'custom.auth.hashers.BCryptPasswordHasher',
]
...

在这种情况下,在验证密码之前,我添加 bcrypt$前缀然后做验证,但在数据库中,密码没有 bcrypt$。 .

它有效,但我想知道是否有其他更简单的方法可以做到这一点,或者有人遇到同样的问题?

我想补充一点,PHP 应用程序和新的 Django 都应该支持这两种格式,我无法对旧版 PHP 进行更改。更改只能在新的 Django 服务器上完成。

最佳答案

您可以使用自定义身份验证后端。
首先在您的 auth 应用程序中创建一个文件(假设您将其命名为 auth),调用文件 backends.py
backends.py 的内容

from django.contrib.auth import get_user_model
from django.contrib.auth.backends import BaseBackend

UserModel = get_user_model()


class ModelBackend(BaseBackend):
"""
Authenticate against the settings ADMIN_LOGIN and ADMIN_PASSWORD.

Use the login name and a hash of the password. For example:

ADMIN_LOGIN = 'admin'
ADMIN_PASSWORD = 'pbkdf2_sha256$30000$Vo0VlMnkR4Bk$qEvtdyZRWTcOsCnI/oQ7fVOu1XAURIZYoOZ3iq8Dr4M='
"""

def authenticate(self, request, username=None, password=None, **kwargs):
if username is None:
username = kwargs.get(UserModel.USERNAME_FIELD)
if username is None or password is None:
return
try:
user = UserModel._default_manager.get_by_natural_key(username)
except UserModel.DoesNotExist:
# Run the default password hasher once to reduce the timing
# difference between an existing and a nonexistent user (#20760).
UserModel().set_password(password)
else:
if user.check_password(password):
# user exists
return user
然后在您的 settings.py 中将您的 backends.py 包含在 AUTHENTICATION_BACKENDS 变量中
它应该看起来像这样
AUTHENTICATION_BACKENDS = [
"djangoprojectname.auth.backends.ModelBackend",
"django.contrib.auth.backends.ModelBackend",
]
最后一点是在你的 auth 模型中实现 check_password 方法,基于我们这里的例子,我们在 auth app 中打开文件 models.py,在 User 类中我们添加了基于 bcrypt 算法实现检查密码的方法。
import bcrypt

class User(AbstractUser):
first_name = models.CharField(max_length=255, null=True)
email = models.CharField(unique=True, max_length=255, blank=True, null=True)

def check_password(self, raw_password):
def setter():
pass

check = bcrypt.checkpw(bytes(raw_password, 'utf-8'), bytes(self.password, 'utf-8'))
return check

def set_password(self, raw_password):
hashed = bcrypt.hashpw(bytes(raw_password, 'utf-8'), bcrypt.gensalt(rounds=10))
encrypted = str(hashed, 'UTF-8')
self.password = encrypted
self._password = encrypted
然后,在您的登录 View 中,您可能必须在 auth 应用程序中的 views.py 中实现类似的功能
username = data.get("username")
password = data.get("password")

if username is None or password is None:
pass # implement for requesting username and password

user = authenticate(username=username, password=password)
if user is None:
pass # implement for invalid credentials

# check user confirmation
confirmed = getattr(user, 'confirmed', None)
if confirmed is False or None:
pass # implement for user not confirmed

# check if user is active
isactive = getattr(user, 'isactive', None)
if isactive is False or None:
pass # implement for user account disabled

login(request, user)

# return view or json response or whatever for login success

关于python - 使用 php 格式的函数 password_hash() 的 Django 密码哈希器,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/57726790/

24 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com