简体   繁体   English

如何在Zend Framework中进行preDispatch转发?

[英]How to do preDispatch forwarding in Zend Framework?

I want to use _forward() in preDispatch after checking if the user is logged in in every controller. 我想在检查用户是否在每个控制器中登录后,在preDispatch使用_forward()

The scenario is quite easy: If the user is not logged in, it should be forwarded to loginAction either in the same controller or in another controller. 这种情况非常简单:如果用户未登录,则应将其转发到同一控制器或另一个控制器中的loginAction

This would cause an infinite loop, as the dispatch process starts over again, calling preDispatch again and the forwarding would start everything again. 这将导致无限循环,因为调度过程再次重新开始,再次调用preDispatch并且转发将再次启动所有内容。

The only solution I was able to come up with was to check if the loginAction is already set in the request. 我能想到的唯一解决方案是检查是否已在请求中设置loginAction

So my question is, how would the sophisticated developer handle this issue? 所以我的问题是,复杂的开发人员将如何处理这个问题?

UPDATE Just after hitting the send button, the ghost of the holy awareness came across ;) Another idea would be to build a LoginController to handle the login request. 更新刚刚点击发送按钮后,神圣意识的幽灵就出现了;)另一个想法是构建一个LoginController来处理登录请求。 Or is there an even better way? 还是有更好的方法?

I use a combination of a Zend_Controller_Plugin and an AuthController to protect my sites. 我使用Zend_Controller_Plugin和AuthController的组合来保护我的网站。 It supports password resets, forced password changes, and automatic account locking, hence the moderate complexity. 它支持密码重置,强制密码更改和自动帐户锁定,因此具有中等复杂性。

Note that I use Doctrine, so this obviously can't just be cut and pasted into your application, but it should function with minimal changes. 请注意,我使用的是Doctrine,因此显然不能将其剪切并粘贴到您的应用程序中,但它应该以最小的更改运行。 I removed a few methods specific to my application, but all the general authentication foo is there. 我删除了一些特定于我的应用程序的方法,但所有通用身份验证foo都在那里。

Plugin 插入

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

Alternatively you could use the following: 或者,您可以使用以下内容:

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

In your controller and then [before] this 在你的控制器然后[之前]这个

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

Effectively skipping the predispatch loop which seems to work fine. 有效地跳过似乎工作正常的predispatch循环。

This process could be in any action, but why not loginAction, or indexAction in LoginController? 这个过程可以在任何动作中,但为什么不在LoginController中使用loginAction或indexAction?

  1. Check for a logged in identity: found? 检查登录身份:找到了吗? redirect to index 重定向到索引
  2. Check for post params: found? 检查邮政参数:发现了吗? validate, set identity or set error messages 验证,设置标识或设置错误消息
  3. Print form 打印表格

Edit: might've been too tired to realize the real problem. 编辑:可能已经太累了,无法实现真正​​的问题。 I'd start out with something like a protected/private member in each login-protected controller, such as protected $authNeeded = true; 我会从每个受登录保护的控制器中的受保护/私有成员开始,例如protected $authNeeded = true; , and check for it in Zend_Controller_Action::init() . ,并在Zend_Controller_Action::init()检查它。 This could lead to repeated code, so another option would be to just perform this code in AuthNeededController which all login-protected controller extends. 这可能会导致重复的代码,因此另一个选择是在AuthNeededController执行此代码,所有受登录保护的控制器都会扩展。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM