我想在检查用户是否在每个控制器中登录后,在_forward()
中使用preDispatch
。
方案非常简单:如果用户未登录,则应将其转发到同一控制器或另一个控制器中的loginAction
。
这将导致无限循环,因为调度过程重新开始,再次调用preDispatch
并且转发将再次启动所有内容。
我能够提出的唯一解决方案是检查请求中是否已设置loginAction
。
所以我的问题是,复杂的开发人员将如何处理这个问题?
更新
刚刚点击发送按钮后,神圣意识的鬼魂就出现了;)
另一个想法是构建一个LoginController
来处理登录请求。或者有更好的方法吗?
答案 0 :(得分:9)
我使用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
}
}
<强> 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');
}
}
}
答案 1 :(得分: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循环。
答案 2 :(得分:1)
这个过程可以在任何动作中,但为什么不在LoginController中使用loginAction或indexAction?
编辑:可能已经太累了,无法意识到真正的问题。我会从每个受登录保护的控制器中的受保护/私有成员开始,例如protected $authNeeded = true;
,并在Zend_Controller_Action::init()
中检查它。这可能会导致重复的代码,因此另一个选择是在AuthNeededController
中执行此代码,所有受登录保护的控制器都会延伸。