Authorization using Zend_Auth


    In this article we will consider usage of one of the Zend Framework components as usual, except for a small deviation from the plan: up to this point there was an archive with the source code of the demo application (this is what was made in result by working with components) which was attached to each article filled under "Developing applications using ZF" heading (i mean russian version of the site). It will not be done that way now - we will consider components separately from each other, because, as it turned out, within the bounds of 2Developers it's been needed to a little of people. Concerning Zend Framework CMS I should say that we started an open project of its development, so details will be covered later in the following articles.

    Thus, authorization using Zend_Auth, "remember me" feature and initialization of a session length (time pending which a user is logged in) will be considered.

    Users table




CREATE TABLE `users` (
  `id` int(10) unsigned NOT NULL auto_increment,
  `login` varchar(36) NOT NULL,
  `pass` varchar(36) NOT NULL,
  PRIMARY KEY  (`id`),
  UNIQUE KEY `login` (`login`)
) ENGINE=MyISAM  DEFAULT CHARSET=utf8 AUTO_INCREMENT=1 ;


    Controller


    Let's add 2 actions in the controller. For instance, UserController:


<?php
 
require_once 'Zend/Controller/Action.php';

class UserController extends Zend_Controller_Action
{    
    
    public function loginAction() 
    {
        // If data was sent
        if(isset($_POST['login']) && !empty($_POST['login'])) {
        
            // Connect to the database and receive the adapter
            require_once 'Zend/Db.php';
            $db_adapter = Zend_Db::factory('pdo_mysql',
                    array('host'     => 'localhost',
                          'username' => 'test',
                          'password' => 'test',
                          'dbname'   => 'test'
                    )
            );
            
            // Receive the model
            require_once(APPPATH."/models/User.php");
            $user_model = new User($db_adapter);
            
            if (true === $message = $user_model->login($_POST['login'], $_POST['pass'])) {
            // Successfully
                $this->_redirect('/');
            } else {
                $this->view->assign('errors', $message);
            }

        }
    }
    //__________________________________________________________
    
    public function logoutAction()
    {
        // Shutdown view script and layout
        $this->_helper->viewRenderer->setNoRender(true);
        Zend_Layout::getMvcInstance()->disableLayout();
        
        require_once 'Zend/Auth.php';
        $auth = Zend_Auth::getInstance();
        // Delete the information from the session
        $auth->clearIdentity();
        Zend_Session::forgetMe();
        $this->_redirect('/');
    }
    //__________________________________________________________

    As you could notice, principal action and operation with Zend_Auth component is held in a model. Therein we will just get the result if all is passed successfully, than a model will return true. Otherwise, we will get the error message.

    Model.



<?php
 
require_once 'Zend/Db/Table/Abstract.php';

class User extends Zend_Db_Table_Abstract
{
    protected $_name = 'users';    
    protected $_primary = array('login');
    
    public function login($login, $pass)
    {
        require_once 'Zend/Auth.php';
        // Receive Zend_Auth instance
        $auth = Zend_Auth::getInstance();
        
        require_once 'Zend/Auth/Adapter/DbTable.php';
    
        // Create Adapter for Zend_Auth, indication where in the database 
        //it should search for login and password for comparison        
        $authAdapter = new Zend_Auth_Adapter_DbTable($this->getAdapter(), 'users', 'login', 'pass', "MD5(?)");

        $authAdapter->setIdentity($login)
                    ->setCredential($pass);

        // Checks and saves the result of authentication
        $result = $auth->authenticate($authAdapter);
        
        if ($result->isValid()) {
        // Successfully

            // It's possible to save the session in additional fields
            $auth->getStorage()->write($authAdapter->getResultRowObject(array('id', 'login')));

            // Receive Zend_Session_Namespace object
            require_once('Zend/Session/Namespace.php');
            $session = new Zend_Session_Namespace('Zend_Auth');
            // Set the time of user logged in
            $session->setExpirationSeconds(24*3600);
            
            // If "remember" was marked
            if (isset($_POST['rememberme'])) {
                Zend_Session::rememberMe();
            }
            return true;
        }
        
        // Abortive
        return $error_msg = $result->getMessages();
    }
}


    Here are several moments not described in ZF manual, that should be addressed.
    Firstly, usage of the fourth parameter while getting the Auth of adapter is the feature which is applied to the password. It's possible to add some salt, but in general the password can be stored abroach so the parameter wouldn't be transfered. Most probably you will not act like this ;)
    Secondly, time when the session is active is often necessary to be more than 15 minutes by default.
    Thirdly, "remember me" feature stores cookies in a client's browser.
   

    View script




<form action="<?php echo $this->url(); ?>" method="post">
<fieldset>
<legend>Sign in</legend>
    
<?php if (sizeof($this->errors)) echo $this->formErrors($this->errors).'<br />'; // Error output (by the way of bulleted list)?>

    <label for="login">Login</label>
    <input type="text" id="login" name="login" value="<?php if (isset($_POST['login'])) echo $_POST['login'];?>" />
    <br /><br />
    
    <label for="pass">Password</label>
    <input type="password" id="pass" name="pass" value="<?php if (isset($_POST['pass']))  echo $_POST['pass'];?>" />
    <br /><br />
    
    <label for="rememberme">Remember me</label>
    <input type="checkbox" id="rememberme" name="rememberme" value="1" />
    <br /><br />

    <input type="submit" value="Submit" />
    <br /><br />

</fieldset>
</form>

    Seems like it's over for general usage.



Created: 17-04-2009 | Modified: 20-04-2009 | by: djaarf | Viewed: 125175

Comments

umpirsky 24-12-2009 11:00
Hi.

Very nice tutorial, thanks.

But, one question. I'm interested in 'remember me' feature. I see you are writing some additional data into session 

$auth->getStorage()->write($authAdapter->getResultRowObject(array('id', 'login')));

and caling  Zend_Session::rememberMe(); 

Is this extra data used to identify that user was logged in before, and how do you check that, I don't see it anywhere in your code?

Regards,
Saša Stamenković.
djaarf 24-12-2009 12:59
Hi Saša.
Thanks for your feedback.

I should have been more clear on this aspect.  Zend_Session::rememberMe() just saves PHPSESSID in cookies. When user returns, browser requests the server to find session with id from cokies. So if the session expired at the server, user won't be logged in.

I think, to get this feature worked, need to create your own algorythm. Maybe store all the sessions that need to remember in DB.
umpirsky 24-12-2009 13:30
Agree, storing some hashes in db and cookie is required, then matching, maybe ip should be invoilved as well.

Regards,
Saša Stamenković.
mehdi 01-06-2010 18:27
hi
please help me
when i click on the submit the page is clear and show only ' / ' .
table user create and enter data.but show only / .
please help me
tanks
rob sperandio 27-12-2009 11:03
What are the filenames and paths of the files you mention above?
mehdi 08-06-2010 10:16
hi
why in the Model. file  the line 
"if ($result->isValid())" is not work and return a value?
please help me
tanks.

Luke 25-10-2010 02:34
mehdi,

if you still haven't figured it out, it's probably a linking problem. You need to read the INSTALL.txt file that comes with the Zend framework. It touches on this.
Gruntle 16-12-2010 21:34
Like the brevity of the article but a couple of points:

You are not escaping user input when redisplaying the form.

You might also end up displaying authentication errors - I think that's a bad idea. Better to display a generic 'FAIL TRY AGAIN' message.

Placing the login method in a db table object doesn't make much sense to me.
John Cartwright 19-01-2011 21:49
Yes I realize this article is several years old, but still relevant -- so for the benefit of anyone coming along a minor improvement:

Why are you passing some $_POST data into the user model, then accessing other $_POST data directly from the superglobal? Wouldn't you agree that the interface would be better served as a third parameter? 

Changes:

$user_model->login($_POST['login'], $_POST['pass'], isset($_POST['rememberme']));

// snip

public function login($login, $pass, $rememberme = false)

// snip

if ($rememberme) {
Zend_Session::rememberMe();
}
Sourabh bajaj 13-08-2011 13:25
Where is the code to determine if the user is already logged in. I mean, suppose a use logged in and set the option of remember me. Then if he comes to login page, he should be automatically redirected to the home page of the site, Right? there should be code at the beginning to check if the session ID is already stored in the cookie.If so, he should be logged in automatically. Anyways, Its a nice tute.
Regards,
And thanks.


Add a new comment



Captcha *

Captcha

Approval required

Для подсветки синтаксиса исходный код следует обрамлять следующими тэгами
<pre><code class="синтаксис" >код</code></pre>
Подерживаются следующие: cpp php javascript sql html-xml css ini