php – 在Zend Framework上处理类似登录页面的最佳方法是什么? (为什么我的实现会爆炸?)

编辑:对不起,这里有大量的代码;我不确定到底发生了什么,所以我加入更多是安全的.

我目前有一个登录页面,它可以提供给中央身份验证服务.我想对用户进行权限检查.如果用户登录,我想将它们重定向登录页面,并让登录页面重定向它们以执行它们最初执行的任何操作,再次运行访问检查.如果他们没有权限,我想将他们重定向到访问被拒绝的页面.

这是我到目前为止所做的:

将此行添加到我的application.ini:

resources.frontController.actionHelperPaths.Cas_Controller_Action_Helper = APPLICATION_PATH "/controllers/helpers"

创建了文件$/ application / controllers / helpers / PermissionRequire.PHP

<?PHP
/**
 * This class is used in order to require that a user have a given privilege before continuing.
 *
 * @copyright 2011 Case Western Reserve University,College of Arts and Sciences
 * @author Billy O'Neal III (bro4@case.edu)
 */

class Cas_Controller_Action_Helper_PermissionRequire extends Zend_Controller_Action_Helper_Abstract
{
    /**
     * Cleans up the supplied list of privileges. Strings are turned into the real privilege objects (Based on name),* privilege objects are left alone.
     *
     * @static
     * @param array|Privilege|string $privileges
     * @return array
     */
    private static function CleanPrivileges($privileges)
    {
        if (!is_array($privileges))
        {
            $privileges =
                    array
                    (
                        $privileges
                    );
        }
        $strings = array_filter($privileges,'is_string');
        $objects = array_filter($privileges,function($o)
        {
            return $o instanceof Privilege;
        });
        $databaSEObjects = PrivilegeQuery::create()->filterByName($strings)->find();
        return array_combine($objects,$databaSEObjects);
    }

    /**
     * Generic implementation for checking whether a user can visit a page.
     * @param Privilege|string|array $privileges Any number of privileges which are required to access the given
     *                                           page. If ANY privilege is held by the user,access is allowed.
     * @param AccessControlList The acl which is being checked. Defaults to the application.
     */
    public function direct($privileges,$acl = null)
    {
        $privileges = self::CleanPrivileges($privileges);
        if ($acl === null)
        {
            $acl = AccessControlListQuery::getApplication();
        }
        $redirector = $this->getActionController()->getHelper('redirector');
        /** @var Zend_Controller_Action_Helper_Redirector $redirector */
        $redirector->setCode(307);
        if (Cas_Model_CurrentUser::IsLoggedIn() && (!Cas_Model_CurrentUser::AccessCheck($acl,$privileges)))
        {
            $redirector->gotoSimple('accessdenied','login');
        }
        else
        {
            $returnData = new Zend_Session_Namespace('Login');
            $returnData->params = $this->getRequest()->getParams();
            $redirector->setGotoSimple('login','login');
            $redirector->redirectAndExit();
        }
    }
}

这是LoginController:

<?PHP

/**
 * LoginController - Controls login access for users
 */

require_once 'CAS.PHP';

class LoginController extends Zend_Controller_Action
{
    /**
     * Logs in to the system,and redirects to the calling action.
     *
     * @return void
     */
    public function loginAction()
    {
        //Authenticate with Login.Case.Edu.
        PHPCAS::client(CAS_VERSION_2_0,'login.case.edu',443,'/cas',false);
        PHPCAS::setNoCasServerValidation();
        PHPCAS::forceAuthentication();

        $user = CaseIdUser::createFromLdap(PHPCAS::getUser());
        Cas_Model_CurrentUser::SetCurrentUser($user->getSecurityIdentifier());

        $returnData = new Zend_Session_Namespace('Login');
        /** @var array $params */
        $redirector = $this->_helper->redirector;
        /** @var Zend_Controller_Action_Helper_Redirector $redirector */
        $redirector->setGotoRoute($returnData->params,'default',true);
        $returnData->unsetAll();
        $redirector->redirectAndExit();
    }

    /**
     * Logs the user out of the system,and redirects them to the index page.
     *
     * @return void
     */
    public function logoutAction()
    {
        Cas_Model_CurrentUser::logout();
        $this->_helper->redirector->gotoRoute('index','index',true);
    }

    /**
     * Returns an access denied view.
     *
     * @return void
     */
    public function accessdeniedAction()
    {
        //Just display the view and punt.
    }
}

问题是,在登录控制器准备URL以重定向用户时,似乎“params”为空.此外,当控制器的POST数据调用$this-> _helper-> permissionRequire(SOME PRIVILEGE)时,这将不起作用.

有没有更好的方法来存储请求的整个状态,并咳嗽与该请求完全匹配的重定向

附:哦,这是一个使用该帮助器的示例控制器:

<?PHP

/**
 * Serves as the index page; does nothing but display views.
 */

class IndexController extends Zend_Controller_Action
{
    public function indexAction()
    {
        $renderer = $this->getHelper('VieWrenderer');
        /** @var $renderer Zend_Controller_Action_Helper_VieWrenderer */
        if (Cas_Model_CurrentUser::IsLoggedIn())
        {
            $this->_helper->permissionRequire(Cas_Model_Privilege::GetLogin());
            $this->render('loggedin');
        }
        else
        {
            $this->render('loggedout');
        }
    }
}

解决方法

因为你非常热衷于保存请求的POST状态,并且因为我自己一直在玩这个相同的想法很长一段时间,如下所示.它仍然没有经过测试,所以我很乐意听到设置这样保存的请求是否真的按预期工作的结果. (此刻懒得试试这个,对不起).

在你的配置中:

resources.frontController.plugins[] = "Cas_Controller_Plugin_Authenticator"

这是插件

class Cas_Controller_Plugin_Authenticator
    extends Zend_Controller_Plugin_Abstract
{
    public function routeStartup( Zend_Controller_Request_Abstract $request )
    {
        if( Zend_Auth::getInstance()->hasIdentity() )
        {
            if( null !== $request->getParam( 'from-login',null ) && Zend_Session::namespaceIsset( 'referrer' ) )
            {
                $referrer = new Zend_Session_Namespace( 'referrer' );
                if( isset( $referrer->request ) && $referrer->request instanceof Zend_Controller_Request_Abstract )
                {
                    Zend_Controller_Front::getInstance()->setRequest( $referrer->request );
                }
                Zend_Session::namespaceUnset( 'referrer' );
            }
        }
        else
        {
            $referrer = new Zend_Session_Namespace( 'referrer' );
            $referrer->request = $this->getRequest();
            return $this->_redirector->gotoRoute(
                array(
                    'module' => 'default','controller' => 'user','action' => 'login'
                ),true
            );
        }
    }
}

插件应检查routeStartup是否用户已通过身份验证;

>如果用户不是:它将当前请求对象保存在会话中并重定向到UserController :: loginAction(). (见下文)
>如果用户IS:它从会话中检索保存的请求对象(如果可用,如果用户刚刚登录则为AND)并替换frontController中的当前请求对象(我应该考虑代理路由器).

总而言之,如果您想要更灵活地确定哪些模块/控制器/操作参数需要身份验证和授权(我想你想要),您可能希望将一些检查移动到另一个钩子而不是routeStartup:即routeShutdown,dispatchLoopStartup或predispatch.因为到那时应该知道行动参数.作为额外的安全措施,您可能还需要比较原始请求的操作参数(模块/控制器/操作)和替换请求,以确定您是否处理了正确保存的请求.

此外,您可能需要在新请求对象的某些或所有挂钩中设置$request-> setdispatched(false).但不完全确定:见docs.

这是一个示例登录控制器:

class UserController
    extends Zend_Controller_Action
{
    public function loginAction()
    {
        $request = $this->getRequest();
        if( $request->isPost() )
        {
            if( someAuthenticationProcessIsValid() )
            {
                if( Zend_Session::namespaceIsset( 'referrer' ) )
                {
                    $referrer = new Zend_Session_Namespace( 'referrer' );
                    if( isset( $referrer->request ) && $referrer->request instanceof Zend_Controller_Request_Abstract )
                    {
                        return $this->_redirector->gotoRoute(
                            array(
                                'module' => $referrer->request->getModuleName(),'controller' => $referrer->request->getControllerName(),'action' => $referrer->request->getActionName(),'from-login' => '1'
                            ),true
                        );
                    }   
                }

                // no referrer found,redirect to default page
                return $this->_redirector->gotoRoute(
                    array(
                        'module' => 'default','controller' => 'index','action' => 'index'
                    ),true
                );
            }
        }

        // GET request or authentication Failed,show login form again
    }
}

出于安全原因,您可能希望设置一个到期跳数为1的会话变量,而不是“from-login”查询字符串变量.

最后,说了这么多;你可能想彻底考虑一下你是否真的想要这种行为.正如您所知,POST请求通常会禁止敏感状态更改操作(创建,删除等).我不确定用户登录后是否正常期望这种行为(在他们的会话刚刚过期之后).此外,您可能想要考虑可能导致应用程序本身出现意外行为的可能情况.我现在想不出任何具体细节,但如果我更多地考虑它,我相信我能想出一些.

HTH

编辑忘记在登录过程后添加正确的重定向操作

相关文章

统一支付是JSAPI/NATIVE/APP各种支付场景下生成支付订单,返...
统一支付是JSAPI/NATIVE/APP各种支付场景下生成支付订单,返...
前言 之前做了微信登录,所以总结一下微信授权登录并获取用户...
FastAdmin是我第一个接触的后台管理系统框架。FastAdmin是一...
之前公司需要一个内部的通讯软件,就叫我做一个。通讯软件嘛...
统一支付是JSAPI/NATIVE/APP各种支付场景下生成支付订单,返...