如何基于与Django内置User一对一模型的password_crypt字段实现认证
嘿,这个需求我之前帮朋友处理过类似的,结合你用的Python2.7和Django1.10版本,咱们完全可以通过自定义认证后端来实现——既能用你自定义模型里的pbkdf2_sha512密码字段做认证,又能完整保留Django内置User的所有功能(比如Token、权限这些)。下面一步步来操作:
1. 编写自定义认证后端
Django的认证系统支持自定义后端,我们只需要实现authenticate()和get_user()两个核心方法就行。在你的app目录下新建一个backends.py文件,写入以下代码:
from django.contrib.auth.backends import ModelBackend from django.contrib.auth.models import User from yourapp.models import YourCustomUserModel # 替换成你的自定义模型名 import hashlib import base64 from django.utils.crypto import constant_time_compare class CustomPasswordBackend(ModelBackend): def authenticate(self, request, username=None, password=None, **kwargs): try: # 先通过用户名找到内置User对象 user = User.objects.get(username=username) # 找到关联的自定义模型实例(替换成你一对一字段的related_name) custom_user = user.your_custom_user_related_name # 解析password_crypt字段:pbkdf2_sha512的格式一般是「算法$迭代次数$盐$哈希值」 algorithm, iterations, salt, hash_val = custom_user.password_crypt.split('$', 3) # 确保是我们需要的pbkdf2_sha512算法 if algorithm != 'pbkdf2_sha512': return None # 用相同参数计算输入密码的哈希值 iterations = int(iterations) derived_key = hashlib.pbkdf2_hmac( 'sha512', password.encode('utf-8'), salt.encode('utf-8'), iterations ) derived_key_b64 = base64.b64encode(derived_key).decode('utf-8') # 用constant_time_compare防止计时攻击,确保密码验证安全 if constant_time_compare(derived_key_b64, hash_val): return user # 返回内置User对象,这样所有内置功能都能正常使用 except (User.DoesNotExist, YourCustomUserModel.DoesNotExist): # 找不到用户或关联模型时返回None return None return None def get_user(self, user_id): # 根据用户ID获取内置User对象,这是认证后端必须实现的方法 try: return User.objects.get(pk=user_id) except User.DoesNotExist: return None
⚠️ 注意:记得替换代码里的YourCustomUserModel为你实际的自定义模型名,还有your_custom_user_related_name要和你一对一关联字段的related_name一致——如果没设置related_name,默认是小写的模型名(比如你的模型叫UserProfile,默认就是userprofile)。
2. 配置Django使用自定义后端
打开项目的settings.py,找到AUTHENTICATION_BACKENDS配置项(如果没有就新建),添加咱们的自定义后端:
AUTHENTICATION_BACKENDS = [ 'yourapp.backends.CustomPasswordBackend', # 优先使用自定义后端 'django.contrib.auth.backends.ModelBackend', # 保留默认后端,以备不时之需 ]
这样Django会依次尝试这些后端进行认证,既支持你的自定义密码字段,也能兼容内置User的密码验证(如果需要的话)。
3. 密码生成工具函数(可选)
如果需要给用户创建/更新密码,得生成符合pbkdf2_sha512格式的哈希值存入password_crypt字段。可以写一个工具函数:
import hashlib import base64 import os def generate_pbkdf2_sha512(password): algorithm = 'pbkdf2_sha512' iterations = 100000 # 迭代次数可根据安全需求调整 salt = base64.b64encode(os.urandom(16)).decode('utf-8') # 生成随机盐 derived_key = hashlib.pbkdf2_hmac( 'sha512', password.encode('utf-8'), salt.encode('utf-8'), iterations ) derived_key_b64 = base64.b64encode(derived_key).decode('utf-8') return f"{algorithm}${iterations}${salt}${derived_key_b64}"
使用示例:
from yourapp.models import YourCustomUserModel from yourapp.utils import generate_pbkdf2_sha512 # 假设函数存在utils.py里 # 假设已经有一个关联的User对象 custom_user = YourCustomUserModel.objects.get(user=existing_user) custom_user.password_crypt = generate_pbkdf2_sha512("user_new_password") custom_user.save()
4. 保留内置User功能的注意事项
- 因为咱们在认证成功后返回的是内置User对象,所以Token认证、权限系统、Admin后台这些功能都能正常使用,完全不受影响。
- 如果你不需要内置User的
password字段,可以把它设为无效值(比如!),防止有人通过默认后端登录,但要确保这个值无法被认证。 - 一对一关联字段建议设置
on_delete=models.CASCADE,确保User对象删除时,自定义模型也会被同步删除。
5. 测试验证
可以在Django Shell里快速测试认证逻辑:
from django.contrib.auth import authenticate # 测试正确密码 user = authenticate(username="test_user", password="correct_password") if user: print("认证成功!") # 测试Token生成(如果用DRF的话) from rest_framework.authtoken.models import Token token, created = Token.objects.get_or_create(user=user) print("生成的Token:", token.key) else: print("认证失败!")
内容的提问来源于stack exchange,提问作者Daniyal Syed
相关产品推荐
相关产品推荐

