2012-07-23 71 views
0

我正致力於開發基於PHP的訪問控制庫,名爲PHP-Bouncer。 PHP-Bouncer允許用戶定義角色列表,每個角色可以訪問哪些頁面,並且每個角色也可以定義覆蓋其他頁面的頁面列表(因此,進入覆蓋頁面會將您重定向到覆蓋頁面) 。這裏是如何做到這一點的工作(從Access Managed Example文檔中)的一個例子:防止訪問控制庫中的無限重定向

$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); 
    } 
} 

有什麼建議嗎?

+0

你應該在第一個'header($ locationString)'之後加上'return'。 – Florent 2012-07-27 15:27:15

+0

即使執行? – 2012-07-27 15:37:05

+0

如果您沒有返回,則可能會發送多個「位置」標題。 – Florent 2012-07-27 15:40:38

回答

1

由於您所需的功能是讓「少數重定向」,我能想到接近這一點的最好辦法是創造或者是$_SESSION(或$_GET我想會工作),你增加你每次發出參數重定向。我應該指出,從用戶的角度來看,這將是相當煩人的處理,但它是你的網站。

假設我們命名爲$_SESSION參數num_redirects

  1. 我們重定向之前,請檢查是否$_SESSION['num_redirects']設置。如果不是,請將其設置爲0
  2. 獲取當前值$_SESSION['num_redirects'],並查看它是否高於重定向閾值。
  3. 如果高於閾值,沒有自動跳轉,只需die();
  4. 如果沒有超過閾值,增加$_SESSION['num_redirects'],存儲回來,和重定向。

因此,該代碼應該是這樣的(我也改善您的網址查詢字符串調用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 
} 

這就是它請注意,您必須添加相同的邏輯在第一次調用!。如果你打算在其他地方重複這個功能,它可能是值得的輔助函數內封裝重定向:

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) 
    )); 
} 

這種方式可以封裝了在該功能中重定向所需的所有邏輯。

+0

爲了徹底性,關於http_build_query的提示和重定向功能。儘管我在庫中使用會話變量的確值得關注。由於我不知道將來誰會使用這個庫(我希望它足夠有用,所以人們想要使用它),我擔心我可能會通過需要會話來干擾某人的設置。這是我的偏執狂還是合法的關注? – 2012-07-28 00:31:14

+0

你總是可以默認爲一個'$ _GET'變量,這個變量保證是交叉兼容的。這取決於你的目標受衆 - 如果你認爲你的用戶可能不需要會話(並且沒有可用),那麼省略它並使用'$ _GET'(記住用戶可以改變它)。但是,隨着Web技術的進步,我認爲不使用某種服務器端數據持久性變得越來越罕見。要求會話很常見,特別是考慮到默認情況下會在PHP中啓用會話擴展。 – nickb 2012-07-28 00:35:17

+0

我決定處理這個問題的最好方法是使其可配置。我創建了一個類似枚舉的類,併爲用戶提供了BouncerProtectionMethod :: Session,BouncerProtectionMethod :: Get和BouncerProtectionMethod :: None之間的選擇。我還爲get或session變量的調用創建了一個變量(如果用戶想在其他地方跟蹤它或正在跟蹤類似的事情)以及最大重定向的變量。 – 2012-07-28 12:04:55