📜 ⬆️ ⬇️

Introduction to Zend_Auth

The article provides an overview of the features of the Zend_Auth component, giving a general idea of ​​the implementation of user authorization in applications based on the Zend Framework. As a basis for the examples given, we used materials from the article “Introduction to the Zend Framework” . The examples are tested on the Zend Framework versions 0.9, 0.9.1 and 0.9.2, and most likely will work with later versions, but not earlier ones.

Posted by: Rob Allen, http://akrabat.com
Original: http://akrabat.com/zend-auth-tutorial
Translation: Alexander Musaev, http://paradigm.ru

PDF-version for printing: http://archive.paradigm.ru/zend_auth.pdf
')

Before you start

The implementation of the user authentication mechanism, which we will use in our examples, is based on PHP sessions. Make sure that the session.save_path parameter in your php.ini set to the directory that the web server can write to.

Authentication

Authentication or authentication is the procedure for verifying the compliance of a subject and the person for whom he is trying to impersonate himself using some unique information. This procedure should be distinguished from identification (identification of the subject of information interaction) and authorization (checking access rights to system resources).

In the context of web applications, authentication usually means checking that a user matches his account on a web server using a login ("login") and password. As an example of the implementation of such a mechanism based on the Zend Framework, we will supplement the base of compact discs with a similar test (a web application implemented in the “Introduction to the Zend Framework” article).

For this we need:
  1. create a table for users in the database (and add a new account to it);
  2. create a login form;
  3. implement a controller containing actions for entry and exit;
  4. add logout to the common fragment of page templates;
  5. add a check that the user is logged in before allowing him to perform any actions.

Table users

The first thing we need is a table in the database for storing user accounts. Its scheme will be as follows:

FieldType ofNull?Field options
idIntegerNoPrimary key, Autoincrement
usernameVarchar (50)NoUnique key
passwordVarchar (50)No-
real_nameVarchar (100)No-


When using MySQL, such a table can be created with the following query:
CREATE TABLE users (
id INT(11) NOT NULL AUTO_INCREMENT,
username VARCHAR(50) NOT NULL,
password VARCHAR(50) NOT NULL,
real_name VARCHAR(100) NOT NULL,
PRIMARY KEY (id),
UNIQUE KEY username (username)
)

We will need to add a test user account to it:
INSERT INTO users (id, username, password, real_name)
VALUES (1, 'rob', 'rob', 'Rob Allen');

Run these SQL queries using any MySQL client program. The username and password can be changed to any other values ​​if desired.

Bootstrap file

To be able to track the registration of users in the system (input and output), we need to use the mechanism of PHP-sessions. For convenient work with it, a special class Zend_Session_Namespace provided in the Zend Framework.

We will need to make the following changes to the bootstrap file:

zf-tutorial/index.php:
...
Zend_Loader::loadClass('Zend_Db_Table');
Zend_Loader::loadClass('Zend_Debug');
Zend_Loader::loadClass('Zend_Auth');
// load configuration
...
// setup database
$dbAdapter = Zend_Db::factory($config->db->adapter,
$config->db->config->asArray());
Zend_Db_Table::setDefaultAdapter($dbAdapter);
Zend_Registry::set('dbAdapter', $dbAdapter);
// setup controller
$frontController = Zend_Controller_Front::getInstance();
...

All you need to do here is to make sure the Zend_Auth class Zend_Auth connected and the dbAdapter database dbAdapter registered. This adapter will be stored in the registry, because later you will need to have access to it from the authorization controller.

Authorization controller

In order to group the actions of entry and exit, we need a special controller. It would be logical to give him the name AuthController . Let's start its implementation with the constructor and define the default action ( indexAction() ):

zf-tutorial/application/controllers/AuthController.php:
<?php
class AuthController extends Zend_Controller_Action
{
function init()
{
$this->initView();
$this->view->baseUrl = $this->_request->getBaseUrl();
}
function indexAction()
{
$this->_redirect('/');
}
}

In the constructor of the controller class, the view is initialized and the value of the baseUrl variable is baseUrl . In addition to the constructor, the class defines an indexAction() action, which is a mandatory requirement for all heirs to Zend_Controller_Action . Considering that we are going to use only loginAction() and logoutAction() , we will not need the default action, so we will redirect the user from the corresponding URL to the home page of the site.

entrance

In order to log in, you need a special form. The login action of the AuthController controller will interact with it in the same way as the IndexController actions work with their forms. The form template is located in the views/scripts/auth/login.phtml , and the data from it will be processed by the AuthController::loginAction() method.

The login form is quite simple and contains only two fields - for the user name and his password.

zf-tutorial/application/views/scripts/auth/login.phtml:
<?php echo $this->render('header.phtml'); ?>
<h1><?php echo $this->escape($this->title); ?></h1>
<?php if(!empty($this->message)) :?>
<div id="message">
<?php echo $this->escape($this->message);?>
</div>
<?php endif; ?>
<form action="<?php echo $this->baseUrl ?>/auth/login" method="post">
<div>
<label for="username">Username</label>
<input type="text" name="username" value=""/>
</div>
<div>
<label for="password">Password</label>
<input type="password" name="password" value=""/>
</div>
<div id="formbutton">
<input type="submit" name="login" value="Login" />
</div>
</form>
<?php echo $this->render('footer.phtml'); ?>

The template displays header.phtml and footer.phtml at the beginning and at the end of the page, respectively. Note that the message from the $this→message variable is displayed only when its value is not empty. This variable is used if an error has occurred at the entrance and the user needs to report it. The rest of the template is the login form itself.

Now that our form is ready, we can move on to creating a controller to work with it.

zf-tutorial/application/controllers/AuthController.php:
class AuthController extends Zend_Controller_Action
{
...
function loginAction()
{
$this->view->message = '';
$this->view->title = "Log in";
$this->render();
}
}

To display the form, you need to set its title and message text, after which the form can be seen by clicking on the URL http://zf-tutorial/auth/login http://zf-tutorial/auth/login . The question arises how, in this case, to process the data sent from it? To do this, we use the same method that was used in the case of forms for editing and adding records to the IndexController . That is, data processing will be performed only if the method of accessing the server is POST. Otherwise, the login action will simply issue the form. The necessary changes to loginAction() are shown below.

zf-tutorial/application/controllers/AuthController.php:
class AuthController extends Zend_Controller_Action
{
...
function loginAction()
{
$this->view->message = '';
if ($this->_request->isPost()) {
// collect the data from the user
Zend_Loader::loadClass('Zend_Filter_StripTags');
$f = new Zend_Filter_StripTags();
$username = $f->filter($this->_request->getPost('username'));
$password = $f->filter($this->_request->
getPost('password'));
if (empty($username)) {
$this->view->message = 'Please provide a username.';
} else {
// setup Zend_Auth adapter for a database table
Zend_Loader::loadClass('Zend_Auth_Adapter_DbTable');
$dbAdapter = Zend_Registry::get('dbAdapter');
$authAdapter = new Zend_Auth_Adapter_DbTable($dbAdapter);
$authAdapter->setTableName('users');
$authAdapter->setIdentityColumn('username');
$authAdapter->setCredentialColumn('password');
// Set the input credential values
// to authenticate against
$authAdapter->setIdentity($username);
$authAdapter->setCredential($password);
// do the authentication
$auth = Zend_Auth::getInstance();
$result = $auth->authenticate($authAdapter);
if ($result->isValid()) {
// success: store database row to auth's storage
// system. (Not the password though!)
$data = $authAdapter->getResultRowObject(null, 'password');
$auth->getStorage()->write($data);
$this->_redirect('/');
} else {
// failure: clear database row from session
$this->view->message = 'Login failed.';
}
}
}
$this->view->title = "Log in";
$this->render();
}
}

Consider the above code step by step:
// collect the data from the user
Zend_Loader::loadClass('Zend_Filter_StripTags');
$f = new Zend_Filter_StripTags();
$username = $f->filter($this->_request->getPost('username'));
$password = $f->filter($this->_request->getPost('password'));

if (empty($username)) {
$this->view->message = 'Please provide a username.';
} else {
...

Here, as usual, we retrieve the username and password from the POST array and process their values ​​with an HTML filter. The getPost() function used in this case automatically checks for the presence of variables set in its parameter and, if not found in POST, returns an empty value.

The authentication process continues only if the username is set. In the case of a null value, attempting to authenticate through Zend_Auth would Zend_Auth an exception.
// setup Zend_Auth adapter for a database table
Zend_Loader::loadClass('Zend_Auth_Adapter_DbTable');
$dbAdapter = Zend_Registry::get('dbAdapter');
$authAdapter = new Zend_Auth_Adapter_DbTable($dbAdapter);
$authAdapter->setTableName('users');
$authAdapter->setIdentityColumn('username');
$authAdapter->setCredentialColumn('password');

To work with authorization data in Zend_Auth , the adapter subsystem is used. Such adapters provide a unified interface to diverse data stores, such as relational databases, LDAP, or simple files. In our example, a database will be used for this purpose, therefore the adapter Zend_Auth_Adapter_DbTable selected. In order to initialize it, you must specify the database parameters (the name of the user table and the names of its fields).
// Set the input credential values to authenticate against
$authAdapter->setIdentity($username);
$authAdapter->setCredential($password);

We must also send to the adapter the exact values ​​of the username and password that were entered into the form.
// do the authentication
$auth = Zend_Auth::getInstance();
$result = $auth->authenticate($authAdapter);

In order to perform the authentication procedure itself, the Zend_Auth class authenticate() method is called. At the same time, the result of authentication is automatically saved in the session.
if ($result->isValid()) {
// success : store database row to auth's storage
// system. (not the password though!)
$data = $authAdapter->getResultRowObject(null,
'password');
$auth->getStorage()->write($data);
$this->_redirect('/');

If the authentication is successful, the user account from the database will be fully stored inside the Zend_Auth singleton (with the exception of the password, of course).
} else {
// failure: clear database row from session
$this->view->message = 'Login failed.';
}
}

In the event that the verification of the username and password failed, we inform the user about this through the message variable. This completes the login process for authentication.

Output

Logging out is much easier than logging in. All that is required for this is to clear the data inside the Zend_Auth singleton. This is implemented in the logoutAction() action of the logoutAction() controller. Thus, to exit you just need to go to the URL http://zftutorial/auth/logout http://zftutorial/auth/logout .

zf-tutorial/application/controllers/AuthController.php:
class AuthController extends Zend_Controller_Action
{
...
function logoutAction()
{
Zend_Auth::getInstance()->clearIdentity();
$this->_redirect('/');
}
}

The logoutAction() function is so trivial that there’s absolutely nothing to comment on.

We will need to provide the user with a special link by clicking on which he would be able to exit the web application. The easiest thing to do is inside the footer template. In addition, we will inform the user of his name, in order to make sure that the authorization was successful. User names are stored in the real_name field of the corresponding database table, and can be accessed from Zend_Auth . The first thing you need to do is pass this value into view, which we will do inside the init() function of the IndexController() controller.

zf-tutorial/application/controllers/IndexController.php:
class IndexController extends Zend_Controller_Action
{
function init()
{
$this->initView();
Zend_Loader::loadClass('Album');
$this->view->baseUrl = $this->_request->getBaseUrl();
$this->view->user = Zend_Auth::getInstance()->getIdentity();
}
...
}

It's very convenient that Zend_Auth is a singleton. Otherwise, in this case, it would be necessary to store its contents in the registry.

Now we need to make changes to the footer.phtml file.

zf-tutorial/application/views/footer.phtml:
<?php if($this->user) : ?>
<p id="logged-in">Logged in as <?php
echo $this->escape($this->user->real_name);?>.
<a href="<?php echo $this->baseUrl ?>/auth/logout">Logout</a></p>
<?php endif; ?>
</div>
</body>
</html>

The code above does not contain anything fundamentally new. We use escape() to make sure that the username is correctly displayed in the browser. The value of the baseUrl variable baseUrl used to correctly form the link.

Functions logout ready.

Action protection

All that is left for us to do is to make sure that no actions will be available until the user registers.

zf-tutorial/application/controllers/IndexController.php:
class IndexController extends Zend_Controller_Action
{
...
function preDispatch()
{
$auth = Zend_Auth::getInstance();
if (!$auth->hasIdentity()) {
$this->_redirect('auth/login');
}
}
...
}

The function with the standard name preDispatch() automatically called before any controller action. Using the hasIdentity() method of the hasIdentity() object, we check whether the login is done. And, if this is not the case, we redirect the user to auth/login .

This work on the authorization is completed.

Conclusion

The example implementation of the Zend Framework user-based authorization functions is quite simple, but you should understand that Zend_Auth still has many useful features that you can use to protect more complex applications with multiple controllers. The authorization system implemented in the Zend_Acl component was not affected as well. The latter is designed to be used in conjunction with Zend_Auth to differentiate the levels of user access to actions or data, but this is a topic for a separate conversation.

All comments on the original article, you can send it to the author at rob@akrabat.com. Comments on the Russian translation should be sent to musayev@yandex.ru.

Update: an archive with examples of programs from the article can be found on the author's website: zend_auth-tutorial_104.zip .

Source: https://habr.com/ru/post/31178/


All Articles