9

我正在使用Zend_AuthWeb 门户中的身份验证。

查询带有loginandpassword列的普通 mySQL“用户”表,并且用户登录。

但是,我还有另外两组要进行身份验证的用户。所有这三个用户组在其他表中都有他们的登录数据。他们的数据来自外部来源,因此不需要将这些登录帐户统一为一个。

因此,一个用户可能同时是三个组中任何一个组的经过身份验证的用户,甚至是三个组中的任何一个

三个登录组中的每一个都有自己的登录表单和注销按钮。

目前,我有一个简单的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 框架的新手。

4

4 回答 4

10

您应该创建自己的 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)
    {
        // ...
    }

当成功通过身份验证时,这将返回登录尝试中匹配的行。因此,您将创建可以返回此行以及 $this->result['groupX'] 标志的 getResult() 方法。就像是:

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

于 2010-08-04T18:02:37.137 回答
3

我从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 适配器),最后将适配器传递给链。

$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
}

这只是基本示例代码,您可能还想在 DbTable 适配器上使用 setCredentialTreatment ...

这种方法的好处是,稍后将其他现有适配器添加到链中以进行其他形式的身份验证(即 OpenID)将是微不足道的......

缺点:因为每次调用 Zend_Auth::getInstance()->getIdentity(); 都会得到一个数组。您当然可以在链适配器中更改它,但这留给您:p。

免责声明:我真的不认为这样做是明智的。为了使其工作,您必须在不同的表中共享相同的登录名和密码,因此如果用户有超过 1 个角色(身份)更改他的密码,您必须确保此更改传播到所有身份表用户有一个帐户。但是我现在不再唠叨了:p。

于 2010-08-09T15:14:32.210 回答
2

因为 Zend_Auth 是一个单例,为每个身份验证源创建自定义身份验证适配器只能解决这个问题的前半部分。问题的后半部分是您希望能够使用多个帐户同时登录:每个身份验证源之一。

最近问了一个类似的问题。解决方案是扩展 Zend_Auth,如接受的答案所示。然后我在我的引导程序中初始化不同的身份验证类型。

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'),等等。

于 2010-12-01T16:26:06.157 回答
1

为什么不创建一个合并所有 3 个表的视图,然后针对该视图进行身份验证?

于 2010-08-06T02:28:01.427 回答