可选择将 REMOTE_USER 传递给应用程序

Optionally pass REMOTE_USER to application

我们公司有一个 apache 服务器 运行 一个 django 应用程序(通过 wsgi)和一些遗留的 php 应用程序。为了进行某种单点登录,我们决定使用 mod_auth_form 和 wsgi 作为 AuthFormProvider。 在 django 本身中,我们启用了 RemoteUserBackend 并且一切正常。 遗留 php 应用程序也通过 mod_auth_form 和 wsgi.

受到保护

问题是有些位置应该由经过身份验证的用户和匿名用户访问,其中经过身份验证的用户有一些额外的东西(比如个人问候语或启用的注销按钮)。

然而,根据我们当前的配置,如果用户之前已登录,我无法告诉 apache 设置 REMOTE_USER,但如果用户未登录则不要求输入密码。

我将尝试举例说明我想要完成的事情。

这是示例配置。

<Location "/protected-zone">
    AuthType basic
    AuthName "private area"
    AuthBasicProvider file
    AuthUserFile /usr/local/etc/apache2/userfile.htaccess

    <RequireAll>
        Require valid-user
    </RequireAll>
</Location>

<Location "/mixed-zone">
    AuthType basic
    AuthName "private area"
    AuthBasicProvider file
    AuthUserFile /usr/local/etc/apache2/userfile.htaccess

    <RequireAll>
        Require [todo]
    </RequireAll>
</Location>

如果用户访问 /protected-zone,系统会要求他们输入密码 - 这很容易。 如果用户随后转到 /mixed-zone(成功登录后),他或她应该会收到他们的用户名(基于 header REMOTE_USER)。

如果未经身份验证的用户访问 /mixed-zone,系统不应提示他或她输入凭据。

到目前为止,我们尝试的是省略 /mixed-zone 中的 ReqireAll 标记,这会导致 apache 永远不会设置 REMOTE_USER,即使用户之前已登录也是如此。

感谢关于 sessions 的评论,我终于能够在我们的特定案例中解决问题。

我们现在使用 sessions,如下例所示。

SessionMaxAge [age in seconds]
SessionCookieName session-cookie path=/;httponly;secure;version=1
SessionCryptoPassphrase [some random string]

<Location "/protected-zone">
    Session On
    SessionEnv On

    AuthType basic
    AuthName "private"
    AuthBasicProvider file
    AuthUserFile /usr/local/etc/apache2/userfile.htaccess

    <RequireAll>
        Require valid-user
    </RequireAll>
</Location>

<Location "/mixed-zone">
    Session On
    SessionEnv On
</Location>

SessionEnv 设置为 On apache 设置一个额外的 HTTP_SESSION header 在底层应用程序可能使用的请求中(参见下面的 PHP 示例)

<?php

$session = array();
parse_str($_SERVER['HTTP_SESSION'], $session);
$username = $session['private-user'];

?>

因为我们使用 django 我写了一个小的中间件,它在 RemoteUserMiddleware 之前执行并根据 session 中的用户设置 REMOTE_USER 如果它是之前没有指定。

from urllib.parse import parse_qs # works in pyhton3

class SessionUserMiddleware (object):
    """
    Middleware to extract a user from a session if no REMOTE_USER is set.
    """
    header = "REMOTE_USER"
    session_header = "HTTP_SESSION"
    session_key = "private-user"

    def process_request(self, request):
        if self.header not in request.META or not request.META[self.header]:
            try:
                username = parse_qs(request.META[self.session_header])[self.session_key]
                request.META[self.header] = username
            except KeyError:
                pass