Django 如何用 REMOTE_USER 接入 IIS、CAS 等外部单点登录
【免费下载链接】djangoThe Web framework for perfectionists with deadlines.项目地址: https://gitcode.com/GitHub_Trending/dj/django
内网应用经常把认证工作交给前置的 Web 服务器或单点登录网关(如 IIS + Integrated Windows Authentication、Apache +mod_authnz_ldap、CAS、WebAuth、mod_auth_sspi等),Django 本身不处理密码,只接收认证后的用户名。这类方案下,服务器会把认证后的用户名放入REMOTE_USER:WSGI 部署中它出现在request.META的REMOTE_USER键,ASGI 部署中则通过 HTTP 头传递,对应request.META的HTTP_REMOTE_USER键。Django 提供RemoteUserMiddleware(或PersistentRemoteUserMiddleware)与RemoteUserBackend来处理这个值,实现自动登录。
官方操作文档见 docs/howto/auth-remote-user.txt,后端 API 说明见 docs/ref/contrib/auth.txt。
基本配置
两步改动都写在项目settings中。
第一步:添加中间件。把django.contrib.auth.middleware.RemoteUserMiddleware加入MIDDLEWARE,且必须放在django.contrib.auth.middleware.AuthenticationMiddleware之后:
MIDDLEWARE = [ "...", "django.contrib.auth.middleware.AuthenticationMiddleware", "django.contrib.auth.middleware.RemoteUserMiddleware", "...", ]这个顺序有硬性要求:如果AuthenticationMiddleware没在前面(即request上还没有user属性),RemoteUserMiddleware会抛出ImproperlyConfigured,提示在 MIDDLEWARE 中把AuthenticationMiddleware插到它前面。
第二步:替换认证后端。在AUTHENTICATION_BACKENDS中用RemoteUserBackend替换ModelBackend:
AUTHENTICATION_BACKENDS = [ "django.contrib.auth.backends.RemoteUserBackend", ]这样配置后,RemoteUserMiddleware会从request.META['REMOTE_USER'](ASGI 下为request.META['HTTP_REMOTE_USER'])取出用户名,通过RemoteUserBackend认证并自动登录该用户。
RemoteUserBackend继承自ModelBackend,所以权限检查逻辑(has_perm、get_all_permissions等)与默认后端一致。默认行为有两点要注意:
create_unknown_user默认为True:数据库中不存在的用户名会被自动创建为User对象;is_active=False的用户不允许认证。如需放行非激活用户,改用django.contrib.auth.backends.AllowAllUsersRemoteUserBackend。
保留 admin 登录通道的可选方案
只配置RemoteUserBackend时,默认ModelBackend被禁用,意味着如果某次请求没有REMOTE_USER值,用户无法登录——包括通过 Django admin 界面登录。如果还需要保留密码登录作为后备,把两个后端都放进列表即可:
AUTHENTICATION_BACKENDS = [ "django.contrib.auth.backends.RemoteUserBackend", "django.contrib.auth.backends.ModelBackend", ]文档明确说明:当REMOTE_USER不存在时回退到ModelBackend,可以解决无法登录 admin 的问题。
另外要清楚边界:contrib.admin的用户管理界面和createsuperuser命令操作的是数据库里存储的用户,与AUTHENTICATION_BACKENDS无关,它们不会和 remote user 流程联动。
使用自定义 HTTP 头(可选分支)
如果你的认证机制传递的不是REMOTE_USER而是自定义 HTTP 头,可以子类化RemoteUserMiddleware并设置header属性为对应的request.META键。例如文档中的例子(放在mysite/middleware.py):
from django.contrib.auth.middleware import RemoteUserMiddleware class CustomHeaderRemoteUserMiddleware(RemoteUserMiddleware): header = "HTTP_AUTHUSER"然后在MIDDLEWARE中用这个自定义中间件替换RemoteUserMiddleware。
这里有一条必须遵守的安全约束(文档以 warning 形式强调):RemoteUserMiddleware绝不能部署在客户端可以自己提供该头的配置中。必须确保 Web 服务器或反向代理基于认证检查结果来设置或剥离该头,绝不允许终端用户提交伪造的头值。由于X-Auth-User和X-Auth_User这类头在request.META中都会归一化为HTTP_X_AUTH_USER,还要确认 Web 服务器不允许用下划线替代连字符来伪造头。
两个环境的差异:
- WSGI:默认配置(
header = "REMOTE_USER")下上述警告不适用,因为request.META中不以HTTP_开头的键只能由 WSGI 服务器设置,HTTP 请求头无法直接写入; - ASGI:所有配置下该警告都适用,因为 ASGI 没有等价于 WSGI environ 的可信值通道。ASGI 部署使用此中间件时必须配合文档所述的反向代理。
只在登录页做外部认证:PersistentRemoteUserMiddleware
RemoteUserMiddleware假设每个已认证请求都携带REMOTE_USER。这在 Basic HTTP Auth(htpasswd之类)下是合理的,但对 Negotiate(GSSAPI/Kerberos)这类开销较大的认证方式,前端服务器通常只对一两个登录 URL 启用认证,登录成功后由应用自己维持会话。
PersistentRemoteUserMiddleware正是为这种场景设计的:它会保持认证会话有效,直到用户显式登出;即使请求中找不到request.META键也不会强制注销(其实现上就是把force_logout_if_no_header设为False)。它可以在上面的配置中作为RemoteUserMiddleware的直接替换使用,中间件位置与后端配置不变。
验证接入是否生效
仓库自带的测试 tests/auth_tests/test_remote_user.py 展示了核对方式,可作为验证思路:
- 向视图发起不带
REMOTE_USER的请求,确认response.context["user"].is_anonymous为True,且User.objects.count()不增加(未提供用户名时不会创建用户); - 带上
REMOTE_USER头再次请求,确认request.user已认证为对应用户,且默认情况下新用户名会在数据库中被自动创建。
测试中 WSGI 客户端通过 environ 直接传入REMOTE_USER值,ASGI 客户端则通过headers传递——这与上文两种环境下request.META键名不同的说明一致。生产环境则应由你的 Web 服务器(IIS、Apache 模块等)在认证后注入该值,Django 侧只负责消费。
需要更多控制时
若默认行为不够用(例如用户名需要清洗 LDAP DN、认证后要按外部目录属性设置用户组),可以继承RemoteUserBackend并重写clean_username(username)和configure_user(request, user, created=True)等方法;configure_user在用户取出或创建后立即调用,created参数可区分是首次创建还是同步已有用户,适合做本地与外部系统的属性同步(如按 LDAP 属性设置用户组)。这些方法的语义见 docs/ref/contrib/auth.txt 中RemoteUserBackend一节,自定义后端思路在 docs/howto/auth-remote-user.txt 中也有说明。
限制与边界
- 认证的最终控制权在前端:用户名被认为是可信的,Django 不校验密码。因此头防伪造是部署侧的硬前提;
is_active=False的用户默认被拒绝,用AllowAllUsersRemoteUserBackend放行是显式选择;- admin 界面与
createsuperuser不感知 remote user 流程,它们管理的是数据库用户; - ASGI 部署必须走反向代理注入/剥离认证头,不能用默认配置直接暴露。
【免费下载链接】djangoThe Web framework for perfectionists with deadlines.项目地址: https://gitcode.com/GitHub_Trending/dj/django
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考