0

我正在开发一个名为PHP-Bouncer的开源基于角色的 PHP 访问控制库。PHP-Bouncer 允许用户定义角色列表,每个角色提供访问哪些页面,并且每个角色还可以定义覆盖其他页面的页面列表(因此转到被覆盖的页面会将您重定向到覆盖页面) . 这是一个如何工作的示例(来自文档中的访问管理示例):

$bouncer = new Bouncer();
// Add a role     Name,      Array of pages role provides
    $bouncer->addRole("Public", array("index.php", "about.php", "fail.php"));
// Add a role          Name,              Array of pages role provides
    $bouncer->addRole("Registered User", array("myaccount.php", "editaccount.php", "viewusers.php"));
// Add a role          Name,   Array of pages role provides       List of pages that are overridden by other pages
    $bouncer->addRole("Admin", array("stats.php", "manageusers.php"), array("viewusers.php" => "manageusers.php"));

// Here we add some users. The user class here extends the BouncerUser class, so it can still do whatever you
// would normally create a user class to do..
    $publicUser         = new User();
    $registeredUser     = new User();
    $adminUser          = new User();
    $registeredAndAdmin = new User();

    $publicUser->addRole("Public");

    $registeredUser->addRole("Public"); // We add the public group to all users since they need it to see index.php
    $registeredUser->addRole("Registered User");

    $adminUser->addRole("Public"); // We add the public group to all users since they need it to see index.php
    $adminUser->addRole("Admin");

    $registeredAndAdmin->addRole("Public"); // We add the public group to all users since they need it to see index.php
    $registeredAndAdmin->addRole("Registered User");
    $registeredAndAdmin->addRole("Admin");

    $bouncer->manageAccess($publicUser->getRoles(), substr($_SERVER["PHP_SELF"], 1), "fail.php");

这是我遇到的问题:在您在上面看到的 manageAccess 函数中,只要角色定义得当,并且所有用户都可以访问 fail.php(或者 fail.php 没有实现 $bouncer 对象),一切都会运行良好。一旦有人创建了一个具有内在冲突的角色(例如用自身覆盖页面)或未能让所有用户访问失败页面,manageAccess 函数就会导致无限循环。由于这很糟糕,我想修复它。但是,我不确定在防止无限循环的同时,允许一些重定向的最佳方法是什么(重定向最多两次或三次可能是理想的行为是可行的)。这是 manageAccess 函数:

/**
 * @param array  $roleList
 * @param string $url
 * @param string $failPage
 */
public function manageAccess($roleList, $url, $failPage = "index.php"){
    $granted = false;
    foreach($roleList as $role){
        if(array_key_exists($role, $this->roles)){
            $obj = $this->roles[$role];
            /** @var $obj BouncerRole */
            $response = $obj->verifyAccess($url);
            if($response->getIsOverridden()){ // If access to the page is overridden forward the user to the overriding page
                $loc            = ($obj->getOverridingPage($url) !== false) ? $obj->getOverridingPage($url) : $failPage;
                $locationString = "Location: ".$loc;
                header($locationString);
                // I broke something in the last commit, perhaps this comment will help?
            }
            if($response->getIsAccessible()){ // If this particular role contains access to the page set granted to true
                $granted = true; // We don't return yet in case another role overrides.
            }
        }
    }
    // If we are here, we know that the page has not been overridden
    // so let's check to see if access has been granted by any of our roles.
    // If not, the user doesn't have access so we'll forward them on to the failure page.
    if(!$granted){
        $locationString = "Location: ".$failPage."?url=".urlencode($url)."&roles=".urlencode(serialize($roleList));
        header($locationString);
    }
}

有什么建议么?

4

1 回答 1

1

由于您想要的功能是允许“一些重定向”,所以我能想到的最好的方法是创建一个$_SESSION(或者$_GET 我认为可以工作的)参数,您每次发出重定向时都会增加该参数。我应该指出,从用户的角度来看,这会很烦人,但这是您的网站。

假设我们将$_SESSION参数命名为num_redirects

  1. 在我们重定向之前,请检查是否$_SESSION['num_redirects']已设置。如果不是,请将其设置为0
  2. 获取 的当前值$_SESSION['num_redirects'],并查看它是否高于重定向阈值。
  3. 如果超过阈值,则不重定向,只需die();
  4. 如果它不高于阈值,则递增$_SESSION['num_redirects'],将其存储回来,然后重定向。

因此,该代码看起来像这样(我还改进了您的 URL 查询字符串,调用http_build_query()

在某个地方,我们需要定义阈值:

define( 'MAX_NUM_REDIRECTS', 3);

然后,我们可以这样做:

// Assuming session_start(); has already occurred
if(!$granted) {
    if( !isset( $_SESSION['num_redirects'])) {
        $_SESSION['num_redirects'] = 0;
    }

    // Check if too many redirects have occurred
    if( $_SESSION['num_redirects'] >= MAX_NUM_REDIRECTS) {
        die( "Severe Error: Misconfigured roles - Maximum number of redirects reached\n");
    }

    // If we get here, we can redirect the user, just add 1 to the redirect count
    $_SESSION['num_redirects'] += 1;

    $query_string = http_build_query( array(
        'url' => $url,
        'roles' => serialize($roleList)
    ));
    $locationString = "Location: " . $failPage . '?' . $query_string;
    header($locationString);
    exit(); // Probably also want to kill the script here
}

就是这样!请注意,您必须为第一次调用添加相同的逻辑header()。如果您要在其他地方重复此功能,则将重定向封装在辅助函数中可能是值得的:

function redirect( $url, array $params = array()) {
    if( !isset( $_SESSION['num_redirects'])) {
        $_SESSION['num_redirects'] = 0;
    }

    // Check if too many redirects have occurred
    if( $_SESSION['num_redirects'] >= MAX_NUM_REDIRECTS) {
        die( "Severe Error: Maximum number of redirects reached\n");
    }

    // If we get here, we can redirect the user, just add 1 to the redirect count
    $_SESSION['num_redirects'] += 1;

    $query_string = http_build_query( $params);
    $locationString = "Location: " . $url . ( !empty( $query_string) ? ('?' . $query_string) : '');
    header($locationString);
    exit(); // Probably also want to kill the script here
}

然后,您可以这样称呼它:

if(!$granted){
    redirect( $failPage, array(
        'url' => $url,
        'roles' => serialize($roleList)
    ));
}

这样,您可以将重定向所需的所有逻辑封装在一个函数中。

于 2012-07-27T22:07:28.367 回答