2010-02-19 20 views
3

我想在preDispatch中檢查用戶是否在每個控制器中登錄後使用_forward()如何在Zend Framework中執行preDispatch轉發?

的情況下是很容易的:如果用戶沒有登錄,就應該可以在同一控制器或其他控制器被轉發到loginAction

這將導致一個無限循環,截至發稿過程再次重新開始,再次調用preDispatch和轉發會重新開始一切。

唯一的解決辦法我能想出是要檢查loginAction在請求中已經設定。

所以我的問題是,複雜的開發人員將如何處理這個問題?

UPDATE 只需點擊發送按鈕之後,神聖的意識鬼碰到;) 另一個想法是建立一個LoginController處理登錄請求。還是有更好的辦法?

回答

9

我使用了一個Zend_Controller_Plugin和一個AuthController的組合來保護我的網站。它支持密碼重置,強制密碼更改以及自動帳戶鎖定,因此中等複雜度。

請注意,我用的原則,所以這顯然不能僅僅被剪切並粘貼到您的應用程序,但它應該用最少的功能的變化。我刪除了一些特定於我的應用程序的方法,但所有一般認證foo都在那裏。

插件

<?php 
class Hobo_Controller_Plugin_Auth extends Zend_Controller_Plugin_Abstract 
{ 
    public function preDispatch(Zend_Controller_Request_Abstract $request) 
    { 
     $auth = Zend_Auth::getInstance(); 
     if ($auth->hasIdentity()) { 
      if ('logout' != $request->getActionName()) { 
       if (! $request->getParam('force_password_change') 
        && $this->_checkPasswordExpiry($auth->getIdentity()->username) 
       ) { 
        $request->setParam('force_password_change', true); 
        $request->setModuleName('default') 
         ->setControllerName('auth') 
         ->setActionName('change-password'); 
       } 
      } 
     } else { 
      // Defer more complex authentication logic to AuthController 
      if ('auth' != $this->getRequest()->getControllerName()) { 
       $redirector = Zend_Controller_Action_HelperBroker::getStaticHelper('redirector'); 
       $redirector->gotoSimple('restricted', 'auth'); 
      } 
     } 
    } 

    protected function _checkPasswordExpiry($username) 
    { 
     // Look up user and return true if password is expired 
    } 
} 

AuthController

<?php 

class AuthController extends Zend_Controller_Action 
{ 
    public function init() 
    { 
     $this->_auth = Zend_Auth::getInstance(); 

     $this->_errorMessenger = new Zend_Controller_Action_Helper_FlashMessenger(); 
     $this->_errorMessenger->setActionController($this)->init(); 
     $this->_errorMessenger->setNamespace('error'); 

     $this->_noticeMessenger = new Zend_Controller_Action_Helper_FlashMessenger(); 
     $this->_noticeMessenger->setActionController($this)->init(); 
     $this->_noticeMessenger->setNamespace('notice'); 

     $this->view->errors = $this->_errorMessenger->getMessages(); 
     $this->view->notices = $this->_noticeMessenger->getMessages(); 
    } 

    public function preDispatch() 
    { 
     if (! $this->_auth->hasIdentity()) { 
      if (! in_array($this->_request->getActionName(), array(
        'logout', 'identify', 'forgot-password', 'reset-password', 'restricted')) 
       ) { 
       $this->_redirect('/auth/restricted'); 
      } 
     } 
    } 

    public function restrictedAction() 
    { 
     // Shows access restricted page 
    } 

    public function logoutAction() 
    { 
     $this->_auth->clearIdentity(); 
     Zend_Session::destroy(); 
     $this->_redirect('/'); 
    } 

    public function identifyAction() 
    { 
     if ($this->_request->isPost()) { 
      $username = $this->_getParam('username'); 
      $password = $this->_getParam('password'); 

      if (empty($username) || empty($password)) { 
       $this->_flashError('Username or password cannot be blank.'); 
      } else { 
       $user = new dUser(); 
       $result = $user->login($username, $password); 

       if ($result->isValid()) { 
        $user->fromArray((array) $this->_auth->getIdentity()); 

        if ($this->_getParam('changepass') || $user->is_password_expired) { 
         $this->_redirect('auth/change-password'); 
         return; 
        } 
        $this->_doRedirect($user); 
        return; 
       } else { 
        $this->_doFailure($result->getIdentity()); 
       } 
      } 
     } 
     $this->_redirect('/'); 
    } 

    public function changePasswordAction() 
    { 
     if ($this->_request->isPost()) { 
      $username = $this->_auth->getIdentity()->username; 
      $formData = $this->_request->getParams(); 

      if (empty($formData['password']) 
       || empty($formData['new_password']) 
       || empty($formData['confirm_password']) 
      ) { 
       $this->_flashError('Password cannot be blank.'); 
       $this->_redirect('auth/change-password'); 
      } elseif ($formData['new_password'] !== $formData['confirm_password']) { 
       $this->_flashError('Password and confirmation do not match.'); 
       $this->_redirect('auth/change-password'); 
      } else { 
       $user = new dUser(); 
       $result = $user->login($username, $formData['password']); 

       if ($result->isValid()) { 

        $user->updatePassword($username, $formData['new_password']); 
        $this->_flashNotice('Password updated successfully!'); 
        $this->_redirect('/'); 
       } else { 
        $this->_flashError('Invalid username or password!'); 
        $this->_redirect('auth/change-password'); 
       } 
      } 

     } 

     if ($this->_getParam('force_password_change')) { 
      $this->view->notice = 'Your password has expired. You must change your password to continue.'; 
     } 
    } 

    public function forgotPasswordAction() 
    { 
     if ($this->_request->isPost()) { 
      // Pseudo-random uppercase 6 digit hex value 
      $resetCode = strtoupper(substr(sha1(uniqid(rand(),true)),0,6)); 

      Doctrine_Query::create() 
       ->update('dUser u') 
       ->set('u.reset_code', '?', array($resetCode)) 
       ->where('u.username = ?', array($this->_getParam('username'))) 
       ->execute(); 

      $this->_doMail($this->_getParam('username'), $resetCode); 

      $this->_flashNotice("Password reset request received."); 
      $this->_flashNotice("An email with further instructions, including your <em>Reset Code</em>, has been sent to {$this->_getParam('username')}."); 
      $this->_redirect("auth/reset-password/username/{$this->_getParam('username')}"); 
     } 
    } 

    public function resetPasswordAction() 
    { 
     $this->view->username = $this->_getParam('username'); 
     $this->view->reset_code = $this->_getParam('reset_code'); 

     if ($this->_request->isPost()) { 
      $formData = $this->_request->getParams(); 
      if (empty($formData['username']) || empty($formData['reset_code'])) { 
       $this->_flashError('Username or reset code cannot be blank.'); 
       $this->_redirect('auth/reset-password'); 
      } elseif ($formData['new_password'] !== $formData['confirm_password']) { 
       $this->_flashError('Password and confirmation do not match.'); 
       $this->_redirect('auth/reset-password'); 
      } else { 
       $user = new dUser(); 
       $result = $user->loginWithResetCode($formData['username'], $formData['reset_code']); 

       if ($result->isValid()) { 
        $user->updatePassword($result->getIdentity(), $formData['new_password']); 

        $user->fromArray((array) $this->_auth->getIdentity()); 

        $this->_flashNotice('Password updated successfully!'); 
        $this->_doRedirect($user); 
       } else { 
        $this->_doFailure($result->getIdentity()); 
        $this->_redirect('auth/reset-password'); 
       } 
      } 
     } 
    } 

    protected function _doRedirect($user) 
    { 
     $this->_helper->Redirector->gotoUserDefault($user); 
    } 

    protected function _flashError($message) 
    { 
     $this->_errorMessenger->addMessage($message); 
    } 

    protected function _flashNotice($message) 
    { 
     $this->_noticeMessenger->addMessage($message); 
    } 

    protected function _doFailure($username) 
    { 
     $user = Doctrine_Query::create() 
      ->from('dUser u') 
      ->select('u.is_locked') 
      ->where('u.username = ?', array($username)) 
      ->fetchOne(); 

     if ($user->is_locked) { 
      $this->_flashError('This account has been locked.'); 
     } else { 
      $this->_flashError('Invalid username or password'); 
     } 
    } 
} 
+0

感謝您的回答!很好的例子,如何快速合理地設置事物。 – PvB 2010-02-20 08:30:04

+0

偉大的工作,$ this - > _ redirect();沒有工作只適用於你的。 – YumYumYum 2010-12-03 09:56:58

1

這個過程可以是任何動作,但爲什麼不能在loginAction,或在的indexAction的LoginController?

  1. 檢查的登錄身份:發現了什麼?重定向到索引
  2. 檢查後的參數:找到了嗎?驗證設置身份或設置錯誤消息
  3. 打印形式

編輯:可能已經太累了,實現了真正的問題。我會從每個登錄保護控制器中的受保護/私有成員開始,如protected $authNeeded = true;,並在Zend_Controller_Action::init()中檢查它。這可能會導致重複的代碼,所以另一種選擇是僅在AuthNeededController中執行所有受登錄保護的控制器擴展的代碼。

+0

感謝您的回答。我會用這個由hobodave提出的設計。他的回答更詳細,所以他得到了答案。 – PvB 2010-02-20 08:33:48

2

另外,您可以使用以下方法:

$request->setParam('skippredispatch',true); 
    $this->_forward('index'); 

在你的控制器,然後按[前]這

// If overwriting jump the pre-dispatchy bits 
if ($request->getParam('skippredispatch')){ 
    $request->setParam('skippredispatch',null); 
    return; 
} 

有效地跳過predispatch循環似乎工作正常。

相關問題