bigywater.tech
Django · 76

自定义认证后端

想支持邮箱登录、token 登录,就要写认证后端。它们的调用顺序是什么?怎么被 login 记住?后端改了配置会发生什么?这篇写三个后端并打日志,把这些行为一一验证。

按顺序
依次尝试,第一个返回用户的胜出;每个后端按参数签名接收凭据
PermissionDenied
后端抛它会立刻终止认证,不再试后面的
静默匿名
session 里记录的后端被从配置里移除后,get_user 返回 AnonymousUser,不报错

调用顺序

实测 · 本机执行输出
username/password 登录 →  u  调用日志: ['TokenBackend.authenticate(token=None)', "ModelBackend.authenticate(username='u')"]
用邮箱 →  u  调用日志: ['TokenBackend.authenticate(token=None)', "ModelBackend.authenticate(username='u@x.com')", "EmailBackend.authenticate(username='u@x.com')"]
token=good →  tok  调用日志: ["TokenBackend.authenticate(token='good')"]
密码错 →  None  调用日志: ['TokenBackend.authenticate(token=None)', "ModelBackend.authenticate(username='u')", "EmailBackend.authenticate(username='u')"]

后端的 authenticate 通常用关键字参数接收凭据(username/password、token…),Django 会跳过签名不匹配的后端(本文的日志里 TokenBackend 收到 token=None 就是这个机制的结果)。

PermissionDenied

实测 · 本机执行输出
token=banned →  None  调用日志: ["TokenBackend.authenticate(token='banned')"]

用于「这个用户被明确禁止,不要再问别的后端了」。

login() 与 backend

实测 · 本机执行输出
直接 login(user)(没有 backend 属性、没有 backend 参数): ValueError: You have multiple authentication backends configured and therefore must provide the `backend` argument or set the `backe
authenticate 返回的 user.backend = proj.be.LoggingModelBackend
session[_auth_user_backend] = proj.be.LoggingModelBackend
显式 backend 参数: proj.be.EmailBackend

只有一个后端时可以省略;多个后端时缺 backend 直接报错。

get_user 与后端变更

实测 · 本机执行输出
get_user(request) → u (通过 session 里记录的后端 ModelBackend.get_user)
把 session 里的后端改成 TokenBackend 后 get_user → u (该后端的 get_user 也能取到,session hash 仍校验)
AnonymousUser

上线时改 AUTHENTICATION_BACKENDS(比如移除旧后端),所有 session 里记着旧后端的用户会被静默登出,且没有任何错误日志。改之前要评估影响。

分步演示:认证后端

认证后端
本步的对象

    本文用到的工具与边界

    • 本机 macOS 27 / Python 3.14.7 / Django 6.1.1(默认用 SQLite,其它数据库会在文中注明),用 django.test.Client 或真实的本机服务器进程实测;计时用 time.perf_counter,数字每次运行会有小幅抖动。SQLite;后端里的日志写到文件
    • 没有验证:异步后端(aauthenticate)、后端里的权限方法(get_all_permissions 等)与多后端的合并规则、RemoteUserBackend、认证后端里的限流
    京ICP备2026067502号-1京公网安备11010502063487号