Django中安全码字段哈希存储与校验,是否需要自定义认证后端?
方案选择建议
两种方案都可以实现需求,可根据你的业务复杂度选择:
方案1:直接在用户模型中新增校验方法(适合业务简单、仅需在少数场景校验安全码的情况)
- 优势:实现成本极低,不需要修改Django认证体系默认配置,逻辑高度内聚在用户模型中
- 实现注意事项:
- 首先需要修改
security_code字段类型,原IntegerField无法存储哈希字符串,需改为CharField,max_length设置为128即可覆盖所有Django默认哈希算法的输出长度 - 直接复用Django内置的密码校验逻辑即可,不需要重复造轮子
- 生成安全码的逻辑中,必须用
make_password处理原始安全码后再存入数据库
- 首先需要修改
- 代码示例:
from django.contrib.auth.hashers import check_password, make_password import random from django.utils import timezone class CustomUser(AbstractBaseUser,PermissionsMixin): phone_number = models.CharField(verbose_name="Mobile Number", max_length=11, unique=True, blank=False, null=False) # 修改字段类型存储哈希值 security_code = models.CharField(verbose_name="Security Code", max_length=128, null=True) security_code_creation = models.DateTimeField(auto_now=True) def check_security_code(self, raw_code: str) -> bool: # 直接复用内置密码校验逻辑,自动处理哈希算法匹配、加盐校验 return check_password(raw_code, self.security_code) # 视图层发送安全码逻辑示例 def send_login_code(request): phone = request.POST.get("phone") user = CustomUser.objects.get(phone_number=phone) # 生成6位随机安全码 raw_code = str(random.randint(100000, 999999)) # 哈希后存入数据库 user.security_code = make_password(raw_code) user.security_code_creation = timezone.now() user.save() # 调用短信接口发送原始安全码给用户 send_sms(phone, f"您的登录验证码是{raw_code},5分钟内有效")
校验时直接调用user.check_security_code(用户输入的验证码)即可得到校验结果。
方案2:自定义认证后端(适合需要兼容Django默认authenticate方法、多登录方式统一入口、依赖Django内置权限体系的场景)
如果你的业务需要用Django自带的authenticate函数统一处理所有登录请求,或者后续还要扩展更多登录方式,推荐自定义认证后端,和密码登录逻辑完全解耦。
- 实现步骤:
- 先按方案1的要求修改用户模型,新增
check_security_code方法 - 新建认证后端类,实现
authenticate和get_user方法 - 在settings.py的
AUTHENTICATION_BACKENDS配置中新增自定义后端
- 先按方案1的要求修改用户模型,新增
- 代码示例:
# 新建auth_backends.py文件 from django.contrib.auth.backends import BaseBackend from django.utils import timezone from .models import CustomUser class SecurityCodeAuthBackend(BaseBackend): def authenticate(self, request, phone=None, code=None, **kwargs): if not phone or not code: return None try: user = CustomUser.objects.get(phone_number=phone) except CustomUser.DoesNotExist: return None # 同时校验安全码和有效期,示例设置5分钟有效期 if user.check_security_code(code) and (timezone.now() - user.security_code_creation).total_seconds() < 300: return user return None def get_user(self, user_id): try: return CustomUser.objects.get(pk=user_id) except CustomUser.DoesNotExist: return None
在settings.py中添加配置:
AUTHENTICATION_BACKENDS = [ # 保留默认的密码登录后端 'django.contrib.auth.backends.ModelBackend', # 新增安全码登录后端 '你的应用名.auth_backends.SecurityCodeAuthBackend', ]
之后安全码登录可以直接调用Django内置的统一登录方法:
from django.contrib.auth import authenticate, login user = authenticate(request, phone=request.POST.get("phone"), code=request.POST.get("code")) if user is not None: login(request, user) # 登录成功后续逻辑
注意必须给安全码加过期校验,避免安全码长期有效导致账户风险,建议有效期设置为1~5分钟。
内容的提问来源于stack exchange,提问作者Mojtaba
相关产品推荐
相关产品推荐

