0 follower

Authenticating User

Our blog application needs to differentiate between the system owner and guest users. Therefore, we need to implement the user authentication feature.

As you may have found that the skeleton application already provides user authentication by checking if the username and password are both demo or admin. In this section, we will modify the corresponding code so that the authentication is done against the User database table.

User authentication is performed in a class implementing the IUserIdentity interface. The skeleton application uses the UserIdentity class for this purpose. The class is stored in the file /wwwroot/blog/protected/components/UserIdentity.php.

Tip: By convention, the name of a class file must be the same as the corresponding class name suffixed with the extension .php. Following this convention, one can refer to a class using a path alias. For example, we can refer to the UserIdentity class with the alias application.components.UserIdentity. Many APIs in Yii can recognize path aliases (e.g. Yii::createComponent()), and using path aliases avoids the necessity of embedding absolute file paths in the code. The existence of the latter often causes trouble when we deploy an application.

We modify the UserIdentity class as follows,

<?php
class UserIdentity extends CUserIdentity
{
    private $_id;
 
    public function authenticate()
    {
        $username=strtolower($this->username);
        $user=User::model()->find('LOWER(username)=?',array($username));
        if($user===null)
            $this->errorCode=self::ERROR_USERNAME_INVALID;
        else if(!$user->validatePassword($this->password))
            $this->errorCode=self::ERROR_PASSWORD_INVALID;
        else
        {
            $this->_id=$user->id;
            $this->username=$user->username;
            $this->errorCode=self::ERROR_NONE;
        }
        return $this->errorCode==self::ERROR_NONE;
    }
 
    public function getId()
    {
        return $this->_id;
    }
}

In the authenticate() method, we use the User class to look for a row in the tbl_user table whose username column is the same as the given username in a case-insensitive manner. Remember that the User class was created using the gii tool in the prior section. Because the User class extends from CActiveRecord, we can exploit the ActiveRecord feature to access the tbl_user table in an OOP fashion.

In order to check if the user has entered a valid password, we invoke the validatePassword method of the User class. We need to modify the file /wwwroot/blog/protected/models/User.php as follows. Note that instead of storing the plain password in the database, we store a hash of the password. When validating the user-entered password, we should compare the hash results, instead. We use the Yii built-in CPasswordHelper to hash the password and to validate it.

class User extends CActiveRecord
{
    ......
    public function validatePassword($password)
    {
        return CPasswordHelper::verifyPassword($password,$this->password);
    }
 
    public function hashPassword($password)
    {
        return CPasswordHelper::hashPassword($password);
    }
}

In the UserIdentity class, we also override the getId() method which returns the id value of the user found in the tbl_user table. The parent implementation would return the username, instead. Both the username and id properties will be stored in the user session and may be accessed via Yii::app()->user from anywhere in our code.

Tip: In the UserIdentity class, we reference the class CUserIdentity without explicitly including the corresponding class file. This is because CUserIdentity is a core class provided by the Yii framework. Yii will automatically include the class file for any core class when it is referenced for the first time.

We also do the same with the User class. This is because the User class file is placed under the directory /wwwroot/blog/protected/models which has been added to the PHP include_path according to the following lines found in the application configuration:

return array(
    ......
    'import'=>array(
        'application.models.*',
        'application.components.*',
    ),
    ......
);

The above configuration says that any class whose class file is located under either /wwwroot/blog/protected/models or /wwwroot/blog/protected/components will be automatically included when the class is referenced for the first time.

The UserIdentity class is mainly used by the LoginForm class to authenticate a user based on the username and password input collected from the login page. The following code fragment shows how UserIdentity is used:

$identity=new UserIdentity($username,$password);
$identity->authenticate();
switch($identity->errorCode)
{
    case UserIdentity::ERROR_NONE:
        Yii::app()->user->login($identity);
        break;
    ......
}

Info: People often get confused about identity and the user application component. The former represents a way of performing authentication, while the latter is used to represent the information related with the current user. An application can only have one user component, but it can have one or several identity classes, depending on what kind of authentication it supports. Once authenticated, an identity instance may pass its state information to the user component so that they are globally accessible via user.

To test the modified UserIdentity class, we can browse the URL http://www.example.com/blog/index.php and try logging in with the username and password that we store in the tbl_user table. If we use the database provided by the blog demo, we should be able to login with username demo and password demo. Note that this blog system does not provide the user management feature. As a result, a user cannot change his account or create a new one through the Web interface. The user management feature may be considered as a future enhancement to the blog application.

Found a typo or you think this page needs improvement?
Edit it on github !