Как лучше всего обрабатывать что-то вроде страницы входа поверх Zend Framework?(И почему моя реализация взрывается?) - PullRequest
4 голосов
/ 04 марта 2011

РЕДАКТИРОВАТЬ: Извините за большое количество кода здесь;Я не уверен, что именно происходит, поэтому я добавил больше, чтобы быть в безопасности.

В настоящее время у меня есть страница входа в систему, которая направляется в центральную службу аутентификации.Я хотел бы проверить права пользователя.Если пользователь не вошел в систему, я бы хотел перенаправить его на страницу входа и сделать так, чтобы страница входа перенаправляла его, чтобы выполнить любое действие, которое он первоначально выполнял, снова запустив проверку доступа.Если у них нет разрешения, я хочу перенаправить их на страницу отказа в доступе.

Вот что я сделал до сих пор:

Добавил эту строку в мои 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', 'default', true);
    }

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

Проблема в том, что в контроллере входа при подготовкеURL для перенаправления пользователя, похоже, «params» - null.Кроме того, это не будет работать, когда есть данные POST для контроллера, вызывающего $this->_helper->permissionRequire(SOME PRIVILEGE).

Есть ли лучший способ сохранить все состояние запроса и выкашливать перенаправление, которое точно соответствует этому запросу?

PS Да, и вот пример контроллера, использующего этот помощник:

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

Ответы [ 4 ]

2 голосов
/ 05 марта 2011

Поскольку вы очень заинтересованы в сохранении состояния запроса POST, а также потому, что я долгое время играл с этой же идеей, как насчет чего-то вроде следующего. Это все еще непроверено, поэтому я хотел бы услышать результат того, действительно ли установка сохраненного запроса работает так, как ожидалось. (Извините, чтобы проверить это в данный момент).

В вашем конфиге ini:

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'
                ),
                'default',
                true
            );
        }
    }
}

Плагин должен проверить routeStartup, аутентифицирован ли пользователь;

  • Если пользователь НЕТ: он сохраняет текущий объект запроса в сеансе и перенаправляет на UserController::loginAction(). (см. ниже)
  • Если пользователь IS: он извлекает сохраненный объект запроса из сеанса (если он доступен, И, если пользователь только что вошел в систему) и заменяет текущий объект запроса в frontController (который, как мне кажется, должен быть подключен к маршрутизатору).

В общем, если вам нужна большая гибкость в определении того, какие параметры модуля / контроллера / действия нуждаются в аутентификации и авторизации (что, я полагаю, вам нужно), вы, вероятно, захотите перенести некоторые проверки на другой хук, чем routeStartup: а именно routeShutdown, dispatchLoopStartup или preDispatch. Потому что к тому времени должны быть известны параметры действий. В качестве дополнительной меры безопасности вы также можете сравнить параметры действия (модуль / контроллер / действие) исходного запроса и запрос на замену, чтобы определить, имеете ли вы дело с правильным сохраненным запросом.

Кроме того, вам может потребоваться установить $request->setDispatched( false ) для нового объекта запроса, в некоторых или во всех хуках. Хотя не совсем уверен: см. документы .

А вот пример контроллера входа в систему:

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'
                            ),
                            'default',
                            true
                        );
                    }   
                }

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

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

В целях безопасности может потребоваться установить переменную сеанса с интервалом истечения 1 вместо переменной строки запроса from-login.

Наконец, сказав все это; Возможно, вы захотите тщательно подумать, действительно ли вы хотите этого поведения в первую очередь. POST-запросы, как вы, конечно, знаете, обычно запрещают чувствительные операции по изменению состояния (создание, удаление и т. Д.). Я не уверен, что пользователи обычно ожидают такого поведения сразу после входа в систему (после истечения срока их сессии). Кроме того, вы можете подумать о возможных сценариях, в которых это может привести к неожиданному поведению самого приложения. Сейчас я не могу придумать каких-либо подробностей, но если бы я больше об этом думал, я уверен, что смогу придумать что-нибудь.

НТН

EDIT
Забыл добавить правильные действия перенаправления после входа в систему

1 голос
/ 05 марта 2011

Это не закодированный ответ, а то, что может помочь. Я столкнулся с подобной проблемой, когда я реализовал с использованием Zend_ACL и Zend_Auth с использованием MySQL-сервера. Несколько раз я сталкивался с тем, что мне нужно, чтобы пользователь, у которого обычно не было доступа к действию контроллера, был предоставлен ему для ситуации. Zend подумал об этом, и поэтому вы можете использовать Assertions , чтобы разрешить доступ там, где обычно было бы отказано в доступе.

Проблема, с которой я столкнулся, заключалась в том, что я установил лимит времени ожидания входа в систему - поэтому, если пользователь покинул компьютер и кто-то пришел, он ничего не мог сделать, так как система вышла из системы.

Проблема в том, что если бы они заполнили форму (есть несколько огромных форм) и заняли больше, чем разрешенное время входа в систему, при отправке формы они приземлились бы на странице входа - очень раздражает, если для заполнения формы потребовалось 30 минут. Следовательно, мне нужно было перенаправить пользователя на действие, которое приняло исходную форму, но каким-то образом опубликовало исходные данные как запрос POST. Мой оригинальный вопрос по этому вопросу.

Так, как я решил это, и, возможно, не лучшее решение, но оно работает. Отсюда пришло вдохновение

Когда пользователь заходит на страницу входа, проверьте его POST Сериализация данных POST и помещение их в сеанс с запрошенным URL . После успешного завершения FORWARD запрос на URL-адрес сохраняется в сеансе. Теперь в правильном месте, проверьте наличие сериализованных данных и снимите сериализацию, установив для данных $ _POST.

Причина, по которой это работает для меня, заключается в том, что когда пользователь отправляет форму входа в систему, вы проверяете информацию о пользователях, выполняете действия, необходимые для их входа в систему, а затем пересылаете их вместо того, чтобы перенаправлять их, таким образом, запрос остается POST - поэтому, когда вы изменяете данные $ _POST на исходный, который вы не сериализовали, страница будет работать как обычно, даже проверяя $this->getRequest()->isPost().
Где я нахожу проблему, и я еще не пытался ее решить, если пользователь нажимает кнопку «Назад», он попадает в форму входа, которую ему не разрешают, так как он теперь вошли в систему, так что получите сообщение о том, что их туда не пускают, было бы лучше, если бы они вернулись к исходной форме - так, чтобы оно пропало через логин.

Если вы думаете, что это поможет, я могу попытаться сократить часть кода, который я использую, чтобы дать представление, как именно я это делаю?

1 голос
/ 04 марта 2011

Когда это GET-запрос, он довольно прост: при отсутствии аутентификации сохраните URL-адрес запроса в сеансе и перенаправьте на страницу входа.После входа в систему, если есть сохраненный URL-адрес запроса, отправьте его туда.Для успешной авторизации, но с ошибками прав доступа, отправьте его прямо сейчас в доступ запрещен.

Важная точка - это запрос POST.Сохранение только URL запроса пропускает данные в посте.Даже если вы также сохраните эти параметры и тип запроса ('POST') в сеансе, возможно ли перенаправление после входа в систему работать как запрос POST?Для меня в этом и заключается суть вопроса.

Одним из решений может быть перенаправление после входа в систему на страницу перехода, содержащую форму (с method="post"), в которой все исходные данные постов спрятаны в скрытом виде.поля.Затем запустите некоторый загружаемый JavaScript, который скрывает кнопку отправки и отправляет форму.В отсутствие javascript он ухудшается - относительно - изящно: пользователь просто должен нажать кнопку отправки.

Будет ли это работать?

0 голосов
/ 04 марта 2011

Я опубликовал то, что я использую в своем проекте для защиты одного контроллера в зависимости от того, зарегистрирован ли пользователь. Следующий код можно использовать для проверки специальных разрешений также при проверке входа в систему. Если у пользователя нет соответствующих привилегий, перенаправьте его на контроллер ошибок или выведите на экран сообщения об ошибках.

В этом коде есть пример того, о чем спрашивается: сохранение текущего URL в параметре при перенаправлении на другой контроллер и перенаправление на тот же URL в случае успеха.

class ProtectedController extends Zend_Controller_Action {

    public function indexAction() {

    if (Zend_Auth::getInstance()->hasIdentity()) {
               //Show the content
       } else {
                $this->_redirect('user/login/?success=' . $this->getRequest()->getRequestUri());
            }
        }

    }

И пользовательский контроллер

class UserController extends Zend_Controller_Action {

    public function loginAction() {
            $returnURL = $this->_getParam('success', '/');
            if (checkAuthenticity()) {
                $this->_redirect($returnURL);
            }
      }
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...