2

コントローラーごとにユーザーがログインしているかどうかを確認してから使用_forward()したい。preDispatch

シナリオは非常に簡単です。ユーザーがログインしていない場合loginAction、同じコントローラーまたは別のコントローラーに転送する必要があります。

これにより、ディスパッチ プロセスが最初からやり直し、再度呼び出しpreDispatch、転送がすべてをやり直すため、無限ループが発生します。

私が思いついた唯一の解決策はloginAction、がリクエストにすでに設定されているかどうかを確認することでした。

私の質問は、洗練された開発者はこの問題をどのように処理するのでしょうか?

更新 送信ボタンを押した直後に、聖なる意識の幽霊が出てきました;) 別のアイデアはLoginController、ログイン要求を処理するために を構築することです。それとももっと良い方法がありますか?

4

3 に答える 3

8

Zend_Controller_Plugin と AuthController を組み合わせてサイトを保護しています。パスワードのリセット、強制的なパスワードの変更、および自動アカウント ロックをサポートしているため、適度な複雑さがあります。

私は Doctrine を使用しているため、これをカット アンド ペーストしてアプリケーションに貼り付けることはできませんが、最小限の変更で機能するはずです。アプリケーションに固有のメソッドをいくつか削除しましたが、一般的な認証 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
    }
}

認証コントローラー

<?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');
        }
    }
}
于 2010-02-20T00:31:41.147 に答える
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 ループを効果的にスキップします。

于 2012-09-03T20:12:50.223 に答える
1

このプロセスは任意のアクションに含まれる可能性がありますが、LoginController で loginAction や indexAction を使用しないのはなぜでしょうか?

  1. ログイン ID を確認します。見つかりましたか? インデックスにリダイレクト
  2. 投稿パラメーターを確認してください: 見つかりましたか? ID の検証、設定、またはエラー メッセージの設定
  3. 印刷フォーム

編集:疲れすぎて本当の問題に気付かなかったかもしれません。など、ログイン保護された各コントローラーで保護された/プライベートメンバーのようなものから始めてprotected $authNeeded = true;、でそれをチェックしZend_Controller_Action::init()ます。これはコードの繰り返しにつながる可能性があるため、AuthNeededControllerログインで保護されたすべてのコントローラーが拡張されているこのコードを実行するという別のオプションがあります。

于 2010-02-19T23:57:46.410 に答える