身份验证

大部分应用都会为用户提供登录或者重置忘记的密码的功能。在Yii2中,缺省情况下,我们没有这个机会。对于basic应用模板,默认情况下,Yii只提供了两个测试用户,这个两个用户是在User模型中写死的。所以,我们必须实现特殊的代码,来使得用户能数据库中登录。

准备

  1. 按照官方指南http://www.yiiframework.com/doc-2.0/guide-start-installation.html的描述,使用Composer包管理器创建一个新的应用。
  2. 在你的配置的组件部分,添加:
  1. 'user' => [
  2. 'identityClass' => 'app\models\User',
  3. 'enableAutoLogin' => true,
  4. ],
  1. 创建一个User表。输入如下命令创建migration:
  1. ./yii migrate/create create_user_table
  1. 更新刚刚创建的migration:
  1. <?php
  2. use yii\db\Schema;
  3. use yii\db\Migration;
  4. class m150626_112049_create_user_table extends Migration
  5. {
  6. public function up()
  7. {
  8. $tableOptions = null;
  9. if ($this->db->driverName === 'mysql') {
  10. $tableOptions = 'CHARACTER SET utf8 COLLATE utf8_general_ci ENGINE=InnoDB';
  11. }
  12. $this->createTable('{{%user}}', [
  13. 'id' => Schema::TYPE_PK,
  14. 'username' => Schema::TYPE_STRING . ' NOT NULL',
  15. 'auth_key' => Schema::TYPE_STRING . '(32) NOT NULL',
  16. 'password_hash' => Schema::TYPE_STRING . ' NOT NULL',
  17. 'password_reset_token' => Schema::TYPE_STRING,
  18. ], $tableOptions);
  19. }
  20. public function down()
  21. {
  22. $this->dropTable('{{%user}}');
  23. }
  24. }
  1. 更新已存在的模型models/User
  1. <?php
  2. namespace app\models;
  3. use yii\db\ActiveRecord;
  4. use yii\web\IdentityInterface;
  5. use yii\base\NotSupportedException;
  6. use Yii;
  7. class User extends ActiveRecord implements IdentityInterface
  8. {
  9. /**
  10. * @inheritdoc
  11. */
  12. public function rules()
  13. {
  14. return [
  15. ['username', 'required'],
  16. ['username', 'unique'],
  17. ['username', 'string', 'min' => 3],
  18. ['username', 'match', 'pattern' =>
  19. '~^[A-Za-z][A-Za-z0-9]+$~', 'message' => 'Username can contain only alphanumeric characters.'],
  20. [['username', 'password_hash',
  21. 'password_reset_token'],
  22. 'string', 'max' => 255
  23. ],
  24. ['auth_key', 'string', 'max' => 32],
  25. ];
  26. }
  27. /**
  28. * @inheritdoc
  29. */
  30. public static function findIdentity($id)
  31. {
  32. return static::findOne($id);
  33. }
  34. public static function findIdentityByAccessToken($token, $type = null)
  35. {
  36. throw new NotSupportedException('"findIdentityByAccessToken" is not implemented.');
  37. }
  38. /**
  39. * Finds user by username
  40. *
  41. * @param string $username
  42. * @return User
  43. */
  44. public static function findByUsername($username)
  45. {
  46. return static::findOne(['username' => $username]);
  47. }
  48. /**
  49. * @inheritdoc
  50. */
  51. public function getId()
  52. {
  53. return $this->getPrimaryKey();
  54. }
  55. /**
  56. * @inheritdoc
  57. */
  58. public function getAuthKey()
  59. {
  60. return $this->auth_key;
  61. }
  62. /**
  63. * @inheritdoc
  64. */
  65. public function validateAuthKey($authKey)
  66. {
  67. return $this->getAuthKey() === $authKey;
  68. }
  69. /**
  70. * Validates password
  71. *
  72. * @param string $password password to validate
  73. * @return boolean if password provided is valid for current
  74. user
  75. */
  76. public function validatePassword($password)
  77. {
  78. return Yii::$app->getSecurity()->validatePassword($password, $this->password_hash);
  79. }
  80. /**
  81. * Generates password hash from password and sets it to the model
  82. *
  83. * @param string $password
  84. */
  85. public function setPassword($password)
  86. {
  87. $this->password_hash =
  88. Yii::$app->getSecurity()->generatePasswordHash($password);
  89. }
  90. /**
  91. * Generates "remember me" authentication key
  92. */
  93. public function generateAuthKey()
  94. {
  95. $this->auth_key =
  96. Yii::$app->getSecurity()->generateRandomString();
  97. }
  98. /**
  99. * Generates new password reset token
  100. */
  101. public function generatePasswordResetToken()
  102. {
  103. $this->password_reset_token =
  104. Yii::$app->getSecurity()->generateRandomString() . '_' . time();
  105. }
  106. /**
  107. * Finds user by password reset token
  108. *
  109. * @param string $token password reset token
  110. * @return static|null
  111. */
  112. public static function findByPasswordResetToken($token)
  113. {
  114. $expire =
  115. Yii::$app->params['user.passwordResetTokenExpire'];
  116. $parts = explode('_', $token);
  117. $timestamp = (int) end($parts);
  118. if ($timestamp + $expire < time()) {
  119. return null;
  120. }
  121. return static::findOne([
  122. 'password_reset_token' => $token
  123. ]);
  124. }
  125. }
  1. 创建一个migration,它会添加一个测试用户:
  1. ./yii migrate/create create_test_user
  1. 更新刚刚创建的migrate:
  1. <?php
  2. use yii\db\Migration;
  3. use app\models\User;
  4. class m150626_120355_create_test_user extends Migration
  5. {
  6. public function up()
  7. {
  8. $testUser = new User();
  9. $testUser->username = 'admin';
  10. $testUser->setPassword('admin');
  11. $testUser->generateAuthKey();
  12. $testUser->save();
  13. }
  14. public function down()
  15. {
  16. User::findByUsername('turbulence')->delete();
  17. return false;
  18. }
  19. }
  1. 安装所有的migration:
  1. ./yii migrate up

如何做…

  1. 访问site/login,输入admin/admin凭证:

身份验证 - 图1

  1. 如果你完成了这些步骤,你就能登录。

工作原理…

  1. 首先,为用户表创建一个migration。除了ID和用户名,我们的表还包含特殊的字段,例如auth_key(主要用途是通过cookie验证用户的身份),password_hash(处于安全原因,我们不能存储密码本身,而应该只是存储密码的hash),以及password_reset_token(当用户需要重置密码时使用)。
  2. 安装和create_test_user migration之后的结果如下图所示:

身份验证 - 图2

我们已经为User模型添加了特殊的方法,并且修改了继承class User extends ActiveRecord implements IdentityInterface,因为我们需要能从数据库中找到用户。

你也可以从高级apphttps://github.com/yiisoft/yii2-appadvanced/blob/master/common/models/User.php中复制用户模型`User。

参考

欲了解更多信息,参考http://www.yiiframework.com/doc-2.0/guide-securityauthentication.html