Django默认ModelBackend不够用,因其仅支持username字段登录且密码必须经check_password()校验,无法满足邮箱、手机号、LDAP或第三方Token等多样化认证需求。

为什么Django默认的ModelBackend不够用?
因为ModelBackend只认username字段,且密码必须走check_password()校验。一旦你要支持邮箱登录、手机号登录、LDAP对接、或第三方Token验证(比如微信小程序code换openid),就必须写自定义Backend。
关键点:Django认证系统不强制你继承某个类,只要实现authenticate()和get_user()两个方法,返回User实例或None即可。
authenticate()里该传哪些参数?
参数由login()或authenticate()调用时传入,Django本身不规定字段名——但request是第一个可选参数(Django 1.10+),之后全是关键字参数。常见写法:
- 邮箱登录:
def authenticate(self, request, email=None, password=None, **kwargs): - 微信登录:
def authenticate(self, request, code=None, **kwargs): - 注意:
**kwargs必须保留,否则Django中间件传来的额外参数(如backend标识)会报错
别在authenticate()里直接raise异常——返回None才是Django期望的“认证失败”信号。
立即学习“Python免费学习笔记(深入)”;
如何安全地处理用户创建与复用?
自定义Backend常要解决“用户不存在就创建”的问题,但得避开竞态条件和重复创建:
- 用
User.objects.get_or_create(email=email, defaults={...}),而不是先get()再create() - 如果邮箱/手机号可能为空(比如微信没留邮箱),别设为
unique=True,改用unique_together或业务层约束 - 创建用户时,务必调用
set_unusable_password()(如果不用密码登录),否则is_authenticated可能因密码校验逻辑意外失败
示例片段:
def authenticate(self, request, email=None, password=None, **kwargs):
if email is None or password is None:
return None
try:
user = User.objects.get(email=email)
if user.check_password(password):
return user
except User.DoesNotExist:
return None
return None
配置AUTHENTICATION_BACKENDS时容易漏什么?
这个配置是元组,不是列表;顺序决定优先级;必须包含至少一个能处理get_user()的Backend:
- 如果你只写了一个自定义Backend,但没保留
django.contrib.auth.backends.ModelBackend,那么request.user在session恢复时会拿不到用户(因为get_user()只在你Backend里实现,而Django默认用第一个Backend找用户) - 正确写法:
AUTHENTICATION_BACKENDS = ['myapp.auth.EmailBackend', 'django.contrib.auth.backends.ModelBackend'] - 调试技巧:在
authenticate()开头加print(f"Called with: {email=}, {password=}"),确认是否被调用;检查settings.py是否被覆盖(比如local_settings.py里漏了)
最常被忽略的是get_user()方法签名——它只接收self和user_id,不能多参数,也不能少参数,否则Django session中间件会静默失败。


















