Zend_Auth: Разрешить пользователю входить в несколько таблиц/идентификаций

Я использую Zend_Auth для аутентификации на веб-портале.

Обычная таблица «пользователей» mySQL со столбцами login и password запрашивается, и пользователь входит в систему.

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

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

Каждая из трех групп входа имеет собственную форму входа и кнопку выхода.

На данный момент у меня есть один простой Zend_Auth логин, взятый из какого-то туториала и немного измененный, который выглядит примерно так:

function login($user, $password)
{

$auth = Zend_Auth::getInstance();
$storage = new Zend_Auth_Storage_Session();

$auth->setStorage($storage);

$adapter = new Zend_Auth_Adapter_DbTable(....);

$adapter->setIdentity($username)->setCredential($password); 

$result = $auth->authenticate($adapter);

if ($result->isValid())
 ......... success!
else 
 .... fail!

с чего мне нужно начать, чтобы заставить это обслуживать и обращаться к отдельным состояниям «входа в систему» ​​для трех групп? Моя идея заключается в том, что я хотел бы поделиться сеансом и управлять аутентификацией отдельно.

Это возможно? Может быть, есть простой префикс, который упрощает эту задачу? Существуют ли какие-либо учебные пособия или ресурсы по этой проблеме?

Я относительный новичок в Zend Framework.


person Pekka    schedule 02.08.2010    source источник
comment
Нужно ли иметь отдельные удостоверения для каждой группы? Или это просто вопрос аутентификации по другой таблице?   -  person Gordon    schedule 02.08.2010
comment
@Gordon они должны быть отдельными личностями (скажем, пользователи CMS, оптовики и конечные пользователи). Я мог бы быть всеми тремя вещами одновременно.   -  person Pekka    schedule 02.08.2010
comment
До ZF это был домен PEAR::LiveUser. devzone.zend.com/node/view/id/1001   -  person mario    schedule 02.08.2010
comment
@leppie: Пожалуйста, воздержитесь от использования оскорбительных и оскорбительных выражений в комментариях к редакции редактирования или где-либо еще на Stack Overflow.   -  person Bill the Lizard    schedule 04.08.2010
comment
Вы хотите объединить три формы входа в одну, т. е. если пользователь находится во всех трех таблицах, есть ли какое-то уникальное имя пользователя, которое их связывает? Или вы хотите сохранить три отдельные формы входа и просто использовать один и тот же код входа для каждой?   -  person Tim Fountain    schedule 04.08.2010
comment
@ Тим нет, есть три отдельные формы входа и отдельные имена пользователей / пароли. Но один пользователь должен иметь возможность войти во все три области одновременно (= используя один и тот же сеанс браузера).   -  person Pekka    schedule 04.08.2010
comment
Является ли Zend_Acl возможным решением? EDIT: Хорошо, теперь, когда я подробно прочитал вопрос, я вижу, что Zend_Acl не поможет вам, если вы хотите аутентифицироваться по 3 таблицам.:/   -  person Fidi    schedule 04.08.2010
comment
@faileN Ммм, не совсем: три таблицы входа в систему взяты из разных источников данных и не должны быть объединены в одну учетную запись пользователя.   -  person Pekka    schedule 04.08.2010
comment
Как вы можете одновременно войти в систему под более чем одним именем? Это не имеет никакого смысла, если вы используете внешние данные (если только вы не проверяете дубликаты учетных записей во всех системах)...   -  person wimvds    schedule 09.08.2010
comment
@wim таблицы входа - это совершенно разные группы пользователей. Думайте об этом как об администраторах, торговых посредниках и конечных пользователях.   -  person Pekka    schedule 09.08.2010
comment
Тогда я действительно не понимаю, почему вы не используете общую пользовательскую таблицу (уникальный идентификатор, имя пользователя, пароль, тип удостоверения - чтобы узнать, на какую таблицу вы должны ссылаться для получения конкретных полей удостоверения) для всех удостоверений, которые вы расширяете в специальные таблицы для каждого типа удостоверения (содержащие уникальный идентификатор и поля, специфичные для этого удостоверения).   -  person wimvds    schedule 09.08.2010
comment
@wim дело в том, что 1) пользовательские данные поступают из трех отдельных таблиц и 2) мне нужно расширить Zend_Auth, чтобы я мог аутентифицироваться в каждой из трех групп пользователей. На данный момент я не могу сказать Zend_Auth Посмотрите, вошел ли текущий пользователь как пользователь из таблицы 3. Я понимаю, к чему вы клоните, и идея неплохая, но я бы хотел избежать мастер-таблицы. . Должна быть возможность сделать Zend_Auth многоэкземплярным — я еще не удосужился опробовать подход Кейна, может быть, это сработает.   -  person Pekka    schedule 09.08.2010
comment
Предположим, что учетная запись пользователя совпадает как в ваших таблицах администраторов, так и в таблицах конечных пользователей, что тогда должно произойти? Будет ли пользователь входить в систему как администратор или как конечный пользователь? Обычно вы просто входите в систему как единая личность (к которой может быть привязано любое количество ролей, поэтому вы можете легко определить, что пользователь может делать с помощью Zend_Acl)... Все было бы иначе - и легко решить - если бы вы имели в виду различные методы входа в систему (т.е. аутентификация через БД, LDAP, OpenID, ...).   -  person wimvds    schedule 09.08.2010
comment
@wim в примере, который вы делаете, пользователь будет входить в систему как администратор и как конечный пользователь, независимо от того, совпадает ли имя учетной записи или нет. В этом весь смысл. Мне нужно, чтобы у одного пользователя были разные роли из разных данных.   -  person Pekka    schedule 09.08.2010
comment
Я думаю, что мой ответ будет работать для вас.   -  person Sonny    schedule 01.12.2010


Ответы (4)


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

Чтобы создать свой Auth Adapter, вы можете взять за основу Zend_Auth_Adapter_DbTable.

Итак, в __construct вместо передачи только одного адаптера DbTable можно передать три адаптера, используемых в каждом ресурсе. Вы будете поступать таким образом, только если каждый из них использует разные ресурсы, такие как LDAP, например, или даже другую базу данных, если нет, вы можете передать только один адаптер и установить три разных имени таблицы в параметрах конфигурации.

Вот пример из Zend_Auth_Adapter_DbTable:

    /**
     * __construct() - Sets configuration options
     *
     * @param  Zend_Db_Adapter_Abstract $zendDb
     * @param  string                   $tableName
     * @param  string                   $identityColumn
     * @param  string                   $credentialColumn
     * @param  string                   $credentialTreatment
     * @return void
     */
    public function __construct(Zend_Db_Adapter_Abstract $zendDb, $tableName = null, $identityColumn = null,
                                $credentialColumn = null, $credentialTreatment = null)
    {
        $this->_zendDb = $zendDb;

        // Here you can set three table names instead of one
        if (null !== $tableName) {
            $this->setTableName($tableName);
        }

        if (null !== $identityColumn) {
            $this->setIdentityColumn($identityColumn);
        }

        if (null !== $credentialColumn) {
            $this->setCredentialColumn($credentialColumn);
        }

        if (null !== $credentialTreatment) {
            $this->setCredentialTreatment($credentialTreatment);
        }
    }

Метод ниже, из Zend_Auth_Adapter_DbTable, пытается аутентифицироваться в одной таблице, вы можете изменить его, чтобы попробовать в трех таблицах, и для каждой, когда вы добьетесь успеха, вы установите это как флаг в частной переменной-члене. Что-то вроде $result['group1'] = 1; Вы установите 1 для каждой успешной попытки входа в систему.

/**
 * authenticate() - defined by Zend_Auth_Adapter_Interface.  This method is called to
 * attempt an authentication.  Previous to this call, this adapter would have already
 * been configured with all necessary information to successfully connect to a database
 * table and attempt to find a record matching the provided identity.
 *
 * @throws Zend_Auth_Adapter_Exception if answering the authentication query is impossible
 * @return Zend_Auth_Result
 */
public function authenticate()
{
    $this->_authenticateSetup();
    $dbSelect = $this->_authenticateCreateSelect();
    $resultIdentities = $this->_authenticateQuerySelect($dbSelect);

    if ( ($authResult = $this->_authenticateValidateResultset($resultIdentities)) instanceof Zend_Auth_Result) {
        return $authResult;
    }

    $authResult = $this->_authenticateValidateResult(array_shift($resultIdentities));
    return $authResult;
}

Вы вернете действительный результат $authresult только в том случае, если одна из трех попыток входа в систему была успешно аутентифицирована.

Теперь в вашем контроллере, после попытки входа в систему:

public function loginAction()
{
    $form = new Admin_Form_Login();

    if($this->getRequest()->isPost())
    {
        $formData = $this->_request->getPost();

        if($form->isValid($formData))
        {

            $authAdapter = $this->getAuthAdapter();
                $authAdapter->setIdentity($form->getValue('user'))
                            ->setCredential($form->getValue('password'));
                $result = $authAdapter->authenticate();

                if($result->isValid()) 
                {
                    $identity = $authAdapter->getResult();
                    Zend_Auth::getInstance()->getStorage()->write($identity);

                    // redirect here
                }           
        }

    }

    $this->view->form = $form;

}

private function getAuthAdapter() 
{   
    $authAdapter = new MyAuthAdapter(Zend_Db_Table::getDefaultAdapter());
    // Here the three tables
    $authAdapter->setTableName(array('users','users2','users3'))
                ->setIdentityColumn('user')
                ->setCredentialColumn('password')
                ->setCredentialTreatment('MD5(?)');
    return $authAdapter;    
} 

Ключевым моментом здесь является строка ниже, которая будет реализована в вашем пользовательском адаптере аутентификации:

$identity = $authAdapter->getResult();

За основу можно взять вот такую ​​форму Zend_Auth_Adapter_DbTable:

   /**
     * getResultRowObject() - Returns the result row as a stdClass object
     *
     * @param  string|array $returnColumns
     * @param  string|array $omitColumns
     * @return stdClass|boolean
     */
    public function getResultRowObject($returnColumns = null, $omitColumns = null)
    {
        // ...
    }

Это возвращает строку, соответствующую попытке входа в систему при успешной аутентификации. Итак, вы создадите свой метод getResult(), который может возвращать эту строку, а также флаги $this->result['groupX']. Что-то типа:

public function authenticate() 
{
    // Perform the query for table 1 here and if ok:
    $this->result = $row->toArrray(); // Here you can get the table result of just one table or even merge all in one array if necessary
    $this->result['group1'] = 1;

    // and so on...
    $this->result['group2'] = 1;

    // ...
    $this->result['group3'] = 1;

   // Else you will set all to 0 and return a fail result
}

public function getResult()
{
    return $this->result;
}

В конце концов, вы можете использовать Zend_Acl, чтобы управлять своими представлениями и другими действиями. Поскольку у вас будут флаги в Zend Auth Storage, вы можете использовать than в качестве ролей:

$this->addRole(new Zend_Acl_Role($row['group1']));

Вот некоторые ресурсы:

http://framework.zend.com/manual/en/zend.auth.introduction.html

http://zendguru.wordpress.com/2008/11/06/zend-framework-auth-with-examples/

http://alex-tech-adventures.com/development/zend-framework/61-zendauth-and-zendform.html

http://alex-tech-adventures.com/development/zend-framework/62-allocation-resources-and-permissions-with-zendacl.html

http://alex-tech-adventures.com/development/zend-framework/68-zendregistry-and-authentication-improvement.html

person Keyne Viana    schedule 04.08.2010
comment
Очень хорошо. Я рассмотрю это, и если я смогу с этим работать, сразу же реализую. Это может занять несколько дней, но я обязательно вернусь с отзывом. - person Pekka; 04.08.2010
comment
+1 пользовательский адаптер аутентификации, вероятно, был бы лучшей идеей. для тех, кто может что и на какой части использовать zend_acl - person robertbasic; 06.08.2010
comment
@Pekka Ты этого добился? Какие-то проблемы? - person Keyne Viana; 11.08.2010
comment
Я до сих пор не удосужился реализовать его из-за насущных других задач. Я награждаю вас 350 баллами за то, что вы первыми предложили законченное и жизнеспособное решение, а также за отличные ссылки. Вторые 350 я присужу вам или wimvds, в зависимости от того, чье решение сработало для меня лучше всего, в ближайшие несколько дней. - person Pekka; 11.08.2010

Я черпал вдохновение из Zym_Auth_Adapter_Chain, но немного изменил его, чтобы он не останавливался на первом успешно возвращенном адаптере.

require_once 'Zend/Auth/Adapter/Interface.php';
require_once 'Zend/Auth/Result.php';

class My_Auth_Adapter_Chain implements Zend_Auth_Adapter_Interface
{
    private $_adapters = array();

    public function authenticate()
    {
        $adapters = $this->getAdapters();

        $results        = array();
        $resultMessages = array();
        foreach ($adapters as $adapter) {
            // Validate adapter
            if (!$adapter instanceof Zend_Auth_Adapter_Interface) {
                require_once 'Zend/Auth/Adapter/Exception.php';
                throw new Zend_Auth_Adapter_Exception(sprintf(
                    'Adapter "%s" is not an instance of Zend_Auth_Adapter_Interface',
                get_class($adapter)));
            }

            $result = $adapter->authenticate();

            if ($result->isValid()) {
                if ($adapter instanceof Zend_Auth_Adapter_DbTable) {
                    $results[] = $adapter->getResultRowObject();
                }
                else {
                    $results[] = $result->getIdentity();
                }
            }
            else {
                $resultMessages[] = $result->getMessages();
            }
        }
        if (!empty($results)) {
            // At least one adapter succeeded, return SUCCESS
            return new Zend_Auth_Result(Zend_Auth_Result::SUCCESS, $results, $resultMessages);
        }

        return new Zend_Auth_Result(Zend_Auth_Result::FAILURE, null, $resultMessages);
    }

    public function getAdapters()
    {
        return $this->_adapters;
    }

    public function addAdapter(Zend_Auth_Adapter_Interface $adapter)
    {
        $this->_adapters[] = $adapter;
        return $this;
    }

    public function setAdapters(array $adapters)
    {
        $this->_adapters = $adapters;
        return $this;
    }
}

Чтобы вызвать его из контроллера, вы просто создаете цепочку, затем адаптеры, которые хотите использовать (в вашем случае это, вероятно, будет адаптер БД для каждой таблицы сущностей) и, наконец, передаете адаптеры в цепочку.

$db = Zend_Db_Table::getDefaultAdapter();

// Setup adapters
$dbAdminsAdapter = new Zend_Auth_Adapter_DbTable($db, 'admins');    
$dbAdminsAdapter->setIdentityColumn('login')
                ->setCredentialColumn('password')
                ->setIdentity($login)
                ->setCredential($password);

$dbUsersAdapter =  new Zend_Auth_Adapter_DbTable($db, 'users');
$dbUsersAdapter->setIdentityColumn('login')
               ->setCredentialColumn('password')
               ->setIdentity($login)
               ->setCredential($password);
...

// Setup chain
$chain = new My_Auth_Adapter_Chain();
$chain->addAdapter($dbAdminsAdapter)
      ->addAdapter($dbUsersAdapter);

// Do authentication
$auth = Zend_Auth::getInstance();
$result = $auth->authenticate($chain);
if ($result->isValid()) {
    // succesfully logged in
}

Это просто базовый пример кода, вы, вероятно, захотите использовать setCredentialTreatment также на адаптерах DbTable...

Преимущество этого подхода заключается в том, что позже будет несложно добавить другие существующие адаптеры для других форм аутентификации (например, OpenID) в цепочку...

Недостаток: как есть, вы будете получать массив в результате каждого вызова Zend_Auth::getInstance()->getIdentity();. Вы, конечно, можете изменить это в цепном адаптере, но это остается за вами :p.

ОТКАЗ ОТ ОТВЕТСТВЕННОСТИ: я действительно не думаю, что разумно делать это таким образом. Чтобы это работало, вы должны использовать один и тот же логин и пароль для разных таблиц, поэтому, если у пользователя более 1 роли (идентификации), он меняет свой пароль, вам необходимо убедиться, что это изменение распространяется на все таблицы идентификации, где это у пользователя есть учетная запись. Но теперь я перестану ворчать :p.

person wimvds    schedule 09.08.2010

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

Недавно я задал похожий вопрос. Решение состояло в том, чтобы расширить Zend_Auth, как показано в принятом ответе. Затем я инициализирую различные типы аутентификации в своем файле bootstrap.

protected function _initAuth()
{
    Zend_Registry::set('auth1', new My_Auth('auth1'));
    Zend_Registry::set('auth2', new My_Auth('auth2'));
    Zend_Registry::set('auth3', new My_Auth('auth3'));
}

Таким образом, вместо синглтона Zend_Auth::getInstance() вы должны использовать Zend_Registry::get('auth1') и т. д.

person Sonny    schedule 01.12.2010

Почему бы просто не создать представление, объединяющее все 3 таблицы, а затем аутентифицироваться в этом представлении?

person iWantSimpleLife    schedule 06.08.2010
comment
Это не даст мне возможности войти ни в одну из трех таблиц одновременно. - person Pekka; 06.08.2010