Authentication
Authentication is checking who is in front of us: a visitor enters a name and a password, the site finds the user and remembers them in the session. In Yii the Yii::$app->user component is responsible for this, and information about a user comes from a class that implements IdentityInterface.
What the project already has
| File | Purpose |
|---|---|
models/User.php | The user class; in the project template users are written right in the code |
models/LoginForm.php | The login form model: checks the name and password |
views/site/login.php | The login form view |
controllers/SiteController.php | The login and logout actions |
The page /site/login already works with the users admin and demo. To store users in the database it is enough to replace the User class.
The users table
php yii migrate/create create_user_tablepublic function safeUp()
{
$this->createTable('user', [
'id' => $this->primaryKey(),
'username' => $this->string(64)->notNull()->unique(),
'password_hash' => $this->string(255)->notNull(),
'auth_key' => $this->string(32)->notNull(),
]);
}After php yii migrate the user table appears. The password is stored in it not as plain text but as a hash, an irreversibly transformed string.
The user class
Replace the content of models/User.php:
// models/User.php
namespace app\models;
use Yii;
use yii\db\ActiveRecord;
use yii\web\IdentityInterface;
class User extends ActiveRecord implements IdentityInterface
{
public static function findIdentity($id)
{
return static::findOne($id);
}
public static function findIdentityByAccessToken($token, $type = null)
{
return null;
}
public function getId()
{
return $this->id;
}
public function getAuthKey()
{
return $this->auth_key;
}
public function validateAuthKey($authKey)
{
return $this->auth_key === $authKey;
}
public static function findByUsername(string $username): ?self
{
return static::findOne(['username' => $username]);
}
public function validatePassword(string $password): bool
{
return Yii::$app->security->validatePassword($password, $this->password_hash);
}
}The first five methods are required by the interface: with them Yii finds a user by the number from the session and checks the "remember me" key. The last two are called by the ready-made LoginForm. The user class is named in config/web.php: 'user' => ['identityClass' => 'app\models\User'].
Creating a user
$user = new User();
$user->username = 'anna';
$user->password_hash = Yii::$app->security->generatePasswordHash('secret123');
$user->auth_key = Yii::$app->security->generateRandomString();
$user->save();Run this code once, for example in a temporary action. Now you can log in on the /site/login page as anna with the password secret123.
Login and logout
Login is done by one line, which is already in LoginForm:
Yii::$app->user->login($user, 3600 * 24 * 30);The second argument is the time in seconds for which the user is remembered after the browser is closed. Logout is Yii::$app->user->logout().
The current user
if (Yii::$app->user->isGuest) {
return $this->redirect(['site/login']);
}
$post->user_id = Yii::$app->user->id;
$name = Yii::$app->user->identity->username;isGuest is true until the visitor logs in. id is the user number, identity is the User object. This is how the author is recorded when a post is created.
Common mistake
A password must not be stored as plain text and compared with ===: if the database leaks, the passwords of all users are exposed. Store only the result of generatePasswordHash() and check with validatePassword().