7

我工作的一家公司要求我使用他们的一个网站并将其设为另一个网站的子域。然后,他们要求我将“登录/注销”会话控制从他们的主域扩展到他们的子域。

完成此操作后,我发现存在控制/管理问题。由于它们有大量的单独页面,并且由于它们广泛的目录结构,它们太复杂了,无法将 PHP 片段添加到每个页面以根据登录状态进行重定向。

这是我的解决方案..请让我知道任何问题或任何其他对我有帮助的事情。

  1. 我将使用 Mod_rewrite 将 subdomin 上的每个请求重定向到特定页面(handler.php?requested_url=)。
  2. 我将在他们的网站上创建一个“站点允许/禁止规则”部分。本节将包含一个带有如下规则的文本框:

     +/weather/            ---> will allow anyone access to any url that contains "/weather/" somewhere within it, irregardless of logged-in status.
    
     -/weather/premium/    ---> will only allow access to a url that contains /weather/premium to logged-in users. 
    

    这将输出到存储在文件rules.php中的数组,如下所示:

    $ruleList = array(); 
    $ruleList[] = '+/weather/'; 
    $ruleList[] = '-/weather/premium/';
    
  3. handler.php中,如果用户登录,我会将它们转发到requested.url。如果用户未登录,那么我将首先假设每个页面都仅限于未登录用户。handler.php将解析request_url 并对照 rules.php检查它,看看是否有任何明确的权限设置。然后,如果规则允许非登录访问,它会将用户转发到requested_url,否则会将它们发送到登录页面。

我可以立即看到的一个问题是,鉴于Mod_rewrite规则会将每个请求发送到handler.php,我该如何避免无限循环?

重定向是否应该通过其他方法完成header("Location: ")

编辑:这是我的斗争的更新:

在顶级域(example.com)的.htaccess文件中,我添加了:

    #Prevent catching requests for the sub1 subdomain
    RewriteCond %{REQUEST_URI} ^sub1\.example\.com
    RewriteRule .* – [L]

然后,在sub1.example.com子域的.htaccess中,我添加了以下内容:

    IndexIgnore *

    RewriteEngine On
    RewriteBase /path/to/base

    #Avoid infinite loop on outgoing requests
    RewriteCond %{HTTP_REFERER} !^$
    RewriteCond %{REQUEST_URI} !^$
    RewriteCond %{HTTP_REFERER} !^/?handler.php?$
    RewriteCond %{REQUEST_URI} !^/?handler.php?$



        #Check for cookie. Redirect to handler if not found.  (not yet implemented)                               
        #RewriteCond %{HTTP_COOKIE} !session_id
    RewriteRule (.*)$ handler.php?requested_url=$1 [NC,L,QSA]

这是handler.php

    <?php

        $url = $_REQUEST['requested_url'];

        //Check list of permissions. For now just assume permitted.
        $permitted = true;
        if ($url == "") $url = "http://sub1.example.com";   
        if ($permitted)
            header("Location: ".$url);
        header("Location: http://sub1.example.com");        

    ?>

我是如此接近,我可以品尝它。不幸的是,目前我几乎在所有地方都得到了“重定向循环”。如果有人可以在正确的方向上轻推我,我将不胜感激!

4

4 回答 4

3

只是一个想法,但也许你不需要为 mod_rewrite 苦苦挣扎。如果您想处理所有来自 PHP 的内容,为什么不将前置文件添加到您的 VHOST 中呢?

php_value auto_prepend_file handler.php

它将包含在任何 PHP 脚本之前,如果需要,您可以重定向。

于 2012-09-30T21:49:01.323 回答
2

您是否有不想使用 apache auth 的原因?我认为它会简单得多。 http://httpd.apache.org/docs/2.2/howto/auth.html

您可以在虚拟主机中按目录指定访问规则,您的用户信息可以在平面文件或数据库中。

于 2012-09-22T04:03:39.157 回答
2

我认为您的想法中有一个循环,因此应用程序中存在循环。像这样:

  1. 用户代理请求子域中的旧资源。

  2. 由于 UA 必须具有有效的凭据并且旧资源无法验证凭据,因此 UA 被重定向到处理程序。

  3. 处理程序验证凭据,并将 UA 重定向回旧资源。

  4. 转到 2。

问题是请求的资源无法区分步骤 1 和步骤 3 中的 UA 请求,因为区别是由 UA 的凭据定义的,旧资源不评估该凭据。

从另一个角度来看,这种矛盾也很明显。想象一下,您解决了循环,并且 UA 被重定向到遗留资源——例如http://sub1.example.com/foo.php。对于服务器返回资源,它必须意味着没有评估凭据(因为它不这样做),因此资源实际上是公共的。

为了解决这个问题,您必须通过更改第 2 步或第 3 步的规则来打破僵局:

  • 要更改第 2 步,请将凭据评估添加到旧资源的响应中。先前的答案建议auto_prepend_file()朝着这个方向发展,但仅适用于 PHP 遗留文件——图像、HTML 等不走运。

  • 要更改第 3 步,找到一种无需 UA 直接请求资源即可交付遗留资源的方法。一种可能性是让处理程序从文件系统中获取资源,并将其readfile()与一些 HTTP 标头管理连接起来。

也许这些的组合会为您解决问题:auto_prepend_file()将身份验证处理应用于遗留 PHP 和readfile()非 PHP 内容。

于 2012-10-01T21:34:19.633 回答
1

您只需要一个简单的验证功能。用户要么被允许访问资源,要么被禁止。所以首先设置上下文:

$rules          = rules_load();
$uri            = $_SERVER['QUERY_STRING'];
$userIsLoggedIn = user_is_logged_in();

接下来我们有上述验证函数,默认情况下应该返回false并将上下文作为参数:

$validation = function (array $rules, $uri, $userIsLoggedIn) {
    $permitted = false;    
    return $permitted;
};

那么逻辑就很简单了:

if ($validation($rules, $uri, $userIsLoggedIn)) {
    # can pass
    echo "can pass";
} else {
    # login first
    echo "login first";
}

这自然已经给了你“先登录”。美好的。我们将很快更改该函数的参数。让我们看看彼此的方式$rules和立场。$uri

每个规则都可以匹配一个 URI,至少路径可以。让我们拆分规则:

 <sign><path>

 sign    := [+-]
 path    := <segment>*/
 segment := /[a-z]+

规则匹配或不匹配$uri. 如果匹配,则标志决定这意味着什么。

所以实际上有两个由符号指定的组,可以说是否$uri匹配。这又是一个简单的函数,首先规则数组按符号过滤,然后按路径过滤。

考虑一个具有三个输入参数的函数,它以与 uri 匹配的数组形式返回所有规则的子集:

function ($sign) use ($rules, $uri) {
    return array_reduce($rules, function ($a, $v) use ($rules, $sign, $uri) {
        $v[0] === $sign && false !== strpos($uri, substr($v, 1)) && $a[] = $v;
        return $a;
    }, array());
};

这或多或少是对array_reduce. 所以让我们假设这将与一个名为的变量相关联$match,然后可以替换该变量$rules$uri作为$validation函数的参数。

$validation = function ($match, $userIsLoggedIn) {

所以剩下的部分就是现在制定验证条件:

    $permitted = $userIsLoggedIn;

默认情况下,如果用户已登录,则授予权限。只有在用户没有登录的情况下,如果-组不匹配,+组匹配,我们可以授予权限。出于安全原因, - 组应该放在第一位并覆盖任何 + 规则:

    $permitted = $userIsLoggedIn ?: !$match('-') && $match('+');

剩下的就是返回该状态:

    return $permitted;
};

由于这个函数相当简单,我们可以发出它。现在完整的代码:

$rules          = rules_load();
$uri            = $_SERVER['QUERY_STRING'];
$userIsLoggedIn = user_is_logged_in();

$match = rules_match($rules, $uri);

$permitted = $userIsLoggedIn ?: !$match('-') && $match('+');

if ($permitted) {
    # can pass
    echo "can pass";
} else {
    # login first
    echo "login first";
}

/**
 */
function rules_match(array $rules, $uri) {
    return function ($sign) use ($rules, $uri) {
        return array_reduce($rules, function ($a, $v) use ($rules, $sign, $uri) {
            $v[0] === $sign && false !== strpos($uri, substr($v, 1)) && $a[] = $v;
            return $a;
        }, array());
    };
}
于 2012-10-01T20:55:15.577 回答