Для начала у нас должен быть уже установлен yii2 basic. Как это сделать описано здесь: Как установить Yii 2 basic, краткая инструкция.
Настраиваем соединение с БД в файле /config/db.php и приступаем.
- Добавляем авторизацию на сайте в Yii2 basic
- Добавляем регистрацию на сайте в Yii2 basic
- Добавляем восстановление пароля на сайте в Yii2 basic
- Авторизация в Yii2 по e-mail вместо username
Добавляем авторизацию на сайте в Yii2 basic
Открываем консоль и добавляем миграцию для создания таблицы User
yii migrate/create create_user_table
Открываем файл с миграцией /migration/m000000_000000_create_user_table.php и добавляем код создания миграции, особо выдумывать не будем и сделаем такую же как и в версии advanced:
<?php use yii\db\Migration; class m000000_000000_create_user_table extends Migration { public function up() { $tableOptions = null; if ($this->db->driverName === 'mysql') { $tableOptions = 'CHARACTER SET utf8 COLLATE utf8_unicode_ci ENGINE=InnoDB'; } $this->createTable('user', [ 'id' => $this->primaryKey(), 'username' => $this->string()->notNull()->unique(), 'auth_key' => $this->string(32)->notNull(), 'password_hash' => $this->string()->notNull(), 'password_reset_token' => $this->string()->unique(), 'email' => $this->string()->notNull()->unique(), 'status' => $this->smallInteger()->notNull()->defaultValue(10), 'created_at' => $this->integer()->notNull(), 'updated_at' => $this->integer()->notNull(), ], $tableOptions); } public function down() { $this->dropTable('user'); } }
После сохранения правок в файле миграции (копируйте только код методов up() и down()), выполняем ее в консоле:
yii migrate
Правим модель /models/User.php:
<?php namespace app\models; use Yii; use yii\base\NotSupportedException; use yii\behaviors\TimestampBehavior; use yii\db\ActiveRecord; use yii\web\IdentityInterface; /** * User model * * @property integer $id * @property string $username * @property string $password_hash * @property string $password_reset_token * @property string $email * @property string $auth_key * @property integer $status * @property integer $created_at * @property integer $updated_at * @property string $password write-only password */ class User extends ActiveRecord implements IdentityInterface { const STATUS_DELETED = 0; const STATUS_ACTIVE = 10; /** * @inheritdoc */ public static function tableName() { return '{{%user}}'; } /** * @inheritdoc */ public function behaviors() { return [ TimestampBehavior::className(), ]; } /** * @inheritdoc */ public function rules() { return [ ['status', 'default', 'value' => self::STATUS_ACTIVE], ['status', 'in', 'range' => [self::STATUS_ACTIVE, self::STATUS_DELETED]], ]; } /** * @inheritdoc */ public static function findIdentity($id) { return static::findOne(['id' => $id, 'status' => self::STATUS_ACTIVE]); } /** * @inheritdoc */ public static function findIdentityByAccessToken($token, $type = null) { throw new NotSupportedException('"findIdentityByAccessToken" is not implemented.'); } /** * Finds user by username * * @param string $username * @return static|null */ public static function findByUsername($username) { return static::findOne(['username' => $username, 'status' => self::STATUS_ACTIVE]); } /** * @inheritdoc */ public function getId() { return $this->getPrimaryKey(); } /** * @inheritdoc */ public function getAuthKey() { return $this->auth_key; } /** * @inheritdoc */ public function validateAuthKey($authKey) { return $this->getAuthKey() === $authKey; } /** * Validates password * * @param string $password password to validate * @return bool if password provided is valid for current user */ public function validatePassword($password) { return Yii::$app->security->validatePassword($password, $this->password_hash); } /** * Generates password hash from password and sets it to the model * * @param string $password */ public function setPassword($password) { $this->password_hash = Yii::$app->security->generatePasswordHash($password); } /** * Generates "remember me" authentication key */ public function generateAuthKey() { $this->auth_key = Yii::$app->security->generateRandomString(); } }
Авторизация готова, теперь добавим пользователя для проверки авторизации. Откроем контроллер /controllers/SiteController.php и добавим экшен в конец контроллера который поможет нам добавить пользователя:
public function actionAddAdmin() { $model = User::find()->where(['username' => 'admin'])->one(); if (empty($model)) { $user = new User(); $user->username = 'admin'; $user->email = 'admin@кодер.укр'; $user->setPassword('admin'); $user->generateAuthKey(); if ($user->save()) { echo 'good'; } } }
Запускаем экшен: site.com/web/index.php?r=site/add-admin и проверяем авторизацию site.com/web/index.php?r=site/login
После успешной аворизации экшен AddAdmin можно удалить за ненадобнастью.
Если вам нужна только авторизация через БД в Yii2 basic, то на этом можно закончить. Если кроме авторизации через БД на сайте нужно добавить регистрацию и восстановление пароля, то продолжим.
Добавляем регистрацию на сайте в Yii2 basic
Для начала создадим модель для форм регистрации, для этого необходимо создать файл /models/SignupForm.php со следующим содержимым:
<?php namespace app\models; use Yii; use yii\base\Model; /** * Signup form */ class SignupForm extends Model { public $username; public $email; public $password; /** * @inheritdoc */ public function rules() { return [ ['username', 'trim'], ['username', 'required'], ['username', 'unique', 'targetClass' => '\app\models\User', 'message' => 'This username has already been taken.'], ['username', 'string', 'min' => 2, 'max' => 255], ['email', 'trim'], ['email', 'required'], ['email', 'email'], ['email', 'string', 'max' => 255], ['email', 'unique', 'targetClass' => '\app\models\User', 'message' => 'This email address has already been taken.'], ['password', 'required'], ['password', 'string', 'min' => 6], ]; } /** * Signs user up. * * @return User|null the saved model or null if saving fails */ public function signup() { if (!$this->validate()) { return null; } $user = new User(); $user->username = $this->username; $user->email = $this->email; $user->setPassword($this->password); $user->generateAuthKey(); return $user->save() ? $user : null; } }
Далее в /controllers/SiteController.php в use добавляем модель регистрации SignupForm и экшен signup:
<?php namespace app\controllers; //... use app\models\LoginForm; use app\models\ContactForm; use app\models\SignupForm; class SiteController extends Controller { //... public function actionSignup() { $model = new SignupForm(); if ($model->load(Yii::$app->request->post())) { if ($user = $model->signup()) { if (Yii::$app->getUser()->login($user)) { return $this->goHome(); } } } return $this->render('signup', [ 'model' => $model, ]); } }
Далее необходимо добавить представление для регистрации, добавляем файл /views/site/signup.php со следующим содержимым:
<?php use yii\helpers\Html; use yii\bootstrap\ActiveForm; $this->title = 'Signup'; $this->params['breadcrumbs'][] = $this->title; ?> <div class="site-signup"> <h1><?= Html::encode($this->title) ?></h1> <p>Please fill out the following fields to signup:</p> <div class="row"> <div class="col-lg-5"> <?php $form = ActiveForm::begin(['id' => 'form-signup']); ?> <?= $form->field($model, 'username')->textInput(['autofocus' => true]) ?> <?= $form->field($model, 'email') ?> <?= $form->field($model, 'password')->passwordInput() ?> <div class="form-group"> <?= Html::submitButton('Signup', ['class' => 'btn btn-primary', 'name' => 'signup-button']) ?> </div> <?php ActiveForm::end(); ?> </div> </div> </div>
И последнее, что необходимо сделать - это добавить регистрацию в меню. Открываем файл /views/layouts/main.php находим виджет меню и добавляем пункт регистрации, после чего виджет с такого вида:
<?php NavBar::begin([ 'brandLabel' => 'My Company', 'brandUrl' => Yii::$app->homeUrl, 'options' => [ 'class' => 'navbar-inverse navbar-fixed-top', ], ]); echo Nav::widget([ 'options' => ['class' => 'navbar-nav navbar-right'], 'items' => [ ['label' => 'Home', 'url' => ['/site/index']], ['label' => 'About', 'url' => ['/site/about']], ['label' => 'Contact', 'url' => ['/site/contact']], Yii::$app->user->isGuest ? ( ['label' => 'Login', 'url' => ['/site/login']] ) : ( '<li>' . Html::beginForm(['/site/logout'], 'post') . Html::submitButton( 'Logout (' . Yii::$app->user->identity->username . ')', ['class' => 'btn btn-link logout'] ) . Html::endForm() . '</li>' ) ], ]); NavBar::end(); ?>
Примет вид:
<?php NavBar::begin([ 'brandLabel' => 'My Company', 'brandUrl' => Yii::$app->homeUrl, 'options' => [ 'class' => 'navbar-inverse navbar-fixed-top', ], ]); $menuItems = [ ['label' => 'Home', 'url' => ['/site/index']], ['label' => 'About', 'url' => ['/site/about']], ['label' => 'Contact', 'url' => ['/site/contact']], ]; if (Yii::$app->user->isGuest) { $menuItems[] = ['label' => 'Signup', 'url' => ['/site/signup']]; $menuItems[] = ['label' => 'Login', 'url' => ['/site/login']]; } else { $menuItems[] = '<li>' . Html::beginForm(['/site/logout'], 'post') . Html::submitButton( 'Logout (' . Yii::$app->user->identity->username . ')', ['class' => 'btn btn-link logout'] ) . Html::endForm() . '</li>'; } echo Nav::widget([ 'options' => ['class' => 'navbar-nav navbar-right'], 'items' => $menuItems, ]); NavBar::end(); ?>
Теперь у нас есть регистрация и авторизация в Yii2 basic.
Для полноценного функционала осталось только сделать сброс и восстановление пароля.
Добавляем восстановление пароля на сайте в Yii2 basic
Для начала необходимо модифицировать модель /models/User.php и добавить в нее несколько методов для создания и проверки токена сброса пароля:
public static function findByPasswordResetToken($token) { if (!static::isPasswordResetTokenValid($token)) { return null; } return static::findOne([ 'password_reset_token' => $token, 'status' => self::STATUS_ACTIVE, ]); } public static function isPasswordResetTokenValid($token) { if (empty($token)) { return false; } $timestamp = (int) substr($token, strrpos($token, '_') + 1); $expire = Yii::$app->params['user.passwordResetTokenExpire']; return $timestamp + $expire >= time(); } public function generatePasswordResetToken() { $this->password_reset_token = Yii::$app->security->generateRandomString() . '_' . time(); } public function removePasswordResetToken() { $this->password_reset_token = null; }
В итоге модель /models/User.php будет выглядеть следующим образом:
<?php namespace app\models; use Yii; use yii\base\NotSupportedException; use yii\behaviors\TimestampBehavior; use yii\db\ActiveRecord; use yii\web\IdentityInterface; class User extends ActiveRecord implements IdentityInterface { const STATUS_DELETED = 0; const STATUS_ACTIVE = 10; public static function tableName() { return '{{%user}}'; } public function behaviors() { return [ TimestampBehavior::className(), ]; } public function rules() { return [ ['status', 'default', 'value' => self::STATUS_ACTIVE], ['status', 'in', 'range' => [self::STATUS_ACTIVE, self::STATUS_DELETED]], ]; } public static function findIdentity($id) { return static::findOne(['id' => $id, 'status' => self::STATUS_ACTIVE]); } public static function findIdentityByAccessToken($token, $type = null) { throw new NotSupportedException('"findIdentityByAccessToken" is not implemented.'); } public static function findByUsername($username) { return static::findOne(['username' => $username, 'status' => self::STATUS_ACTIVE]); } public static function findByPasswordResetToken($token) { if (!static::isPasswordResetTokenValid($token)) { return null; } return static::findOne([ 'password_reset_token' => $token, 'status' => self::STATUS_ACTIVE, ]); } public static function isPasswordResetTokenValid($token) { if (empty($token)) { return false; } $timestamp = (int) substr($token, strrpos($token, '_') + 1); $expire = Yii::$app->params['user.passwordResetTokenExpire']; return $timestamp + $expire >= time(); } public function getId() { return $this->getPrimaryKey(); } public function getAuthKey() { return $this->auth_key; } public function validateAuthKey($authKey) { return $this->getAuthKey() === $authKey; } public function validatePassword($password) { return Yii::$app->security->validatePassword($password, $this->password_hash); } public function setPassword($password) { $this->password_hash = Yii::$app->security->generatePasswordHash($password); } public function generateAuthKey() { $this->auth_key = Yii::$app->security->generateRandomString(); } public function generatePasswordResetToken() { $this->password_reset_token = Yii::$app->security->generateRandomString() . '_' . time(); } public function removePasswordResetToken() { $this->password_reset_token = null; } }
Метод проверки токена сброса пароля isPasswordResetTokenValid используем переменную user.passwordResetTokenExpire для отмены токена сброса пароля, ее необходимо указать в файле /congif/params.php:
<?php return [ 'adminEmail' => 'admin@example.com', 'user.passwordResetTokenExpire' => 3600, ];
Далее необходимо добавить модель формы запроса на смену пароля и модель формы смены пароля. Итого 2 модели. Для начала добавим модель формы запроса на смену пароля /models/PasswordResetRequestForm.php со следующим содержимым:
<?php namespace app\models; use Yii; use yii\base\Model; /** * Password reset request form */ class PasswordResetRequestForm extends Model { public $email; /** * @inheritdoc */ public function rules() { return [ ['email', 'trim'], ['email', 'required'], ['email', 'email'], ['email', 'exist', 'targetClass' => '\app\models\User', 'filter' => ['status' => User::STATUS_ACTIVE], 'message' => 'There is no user with such email.' ], ]; } /** * Sends an email with a link, for resetting the password. * * @return bool whether the email was send */ public function sendEmail() { /* @var $user User */ $user = User::findOne([ 'status' => User::STATUS_ACTIVE, 'email' => $this->email, ]); if (!$user) { return false; } if (!User::isPasswordResetTokenValid($user->password_reset_token)) { $user->generatePasswordResetToken(); if (!$user->save()) { return false; } } return Yii::$app ->mailer ->compose( ['html' => 'passwordResetToken-html', 'text' => 'passwordResetToken-text'], ['user' => $user] ) ->setFrom([Yii::$app->params['supportEmail'] => Yii::$app->name . ' robot']) ->setTo($this->email) ->setSubject('Password reset for ' . Yii::$app->name) ->send(); } }
Модель будет отправлять письмо с подтверждением смены пароля. Для этого она использует 2 лайоута (html и text), html у нас уже есть, необходимо добавить text. Для этого создадим файл /mail/layouts/text.php со следующим содержимым:
<?php use yii\helpers\Html; ?> <?php $this->beginPage() ?> <?php $this->beginBody() ?> <?= $content ?> <?php $this->endBody() ?> <?php $this->endPage() ?>
Также необходимо создать 2 представляения самого письма (html и text версии). Создадим html версию представления письма, для этого создаем файл /mail/passwordResetToken-html.php со следующим содержимым:
<?php use yii\helpers\Html; $resetLink = Yii::$app->urlManager->createAbsoluteUrl(['site/reset-password', 'token' => $user->password_reset_token]); ?> <div class="password-reset"> <p>Hello <?= Html::encode($user->username) ?>,</p> <p>Follow the link below to reset your password:</p> <p><?= Html::a(Html::encode($resetLink), $resetLink) ?></p> </div>
И представление для версии text /mail/passwordResetToken-text.php:
<?php $resetLink = Yii::$app->urlManager->createAbsoluteUrl(['site/reset-password', 'token' => $user->password_reset_token]); ?> Hello <?= $user->username ?>, Follow the link below to reset your password: <?= $resetLink ?>
В качестве e-mail отправителя модель указывает e-mail из параметра supportEmail, его тоже нужно добавить. Для этого откройте на редактирование файл /config/params.php и добавьте в массив элемент supportEmail, в итоге получите примерно следующее:
<?php return [ 'adminEmail' => 'admin@devreadwrite.com', 'user.passwordResetTokenExpire' => 3600, 'supportEmail' => 'robot@devreadwrite.com' ];
Далее модель для формы смены пароля /models/ResetPasswordForm.php со следующим содержимым:
<?php namespace app\models; use yii\base\Model; use yii\base\InvalidParamException; /** * Password reset form */ class ResetPasswordForm extends Model { public $password; /** * @var \app\models\User */ private $_user; /** * Creates a form model given a token. * * @param string $token * @param array $config name-value pairs that will be used to initialize the object properties * @throws \yii\base\InvalidParamException if token is empty or not valid */ public function __construct($token, $config = []) { if (empty($token) || !is_string($token)) { throw new InvalidParamException('Password reset token cannot be blank.'); } $this->_user = User::findByPasswordResetToken($token); if (!$this->_user) { throw new InvalidParamException('Wrong password reset token.'); } parent::__construct($config); } /** * @inheritdoc */ public function rules() { return [ ['password', 'required'], ['password', 'string', 'min' => 6], ]; } /** * Resets password. * * @return bool if password was reset. */ public function resetPassword() { $user = $this->_user; $user->setPassword($this->password); $user->removePasswordResetToken(); return $user->save(false); } }
Теперь добавим экшены для работы с моделями описаными выше. Открываем контроллер /controllers/SiteController.php в use добаляем созданные модели и 2 действия (actionRequestPasswordReset и actionResetPassword):
<?php namespace app\controllers; //... use app\models\SignupForm; use app\models\PasswordResetRequestForm; use app\models\ResetPasswordForm; class SiteController extends Controller { //... /** * Requests password reset. * * @return mixed */ public function actionRequestPasswordReset() { $model = new PasswordResetRequestForm(); if ($model->load(Yii::$app->request->post()) && $model->validate()) { if ($model->sendEmail()) { Yii::$app->session->setFlash('success', 'Check your email for further instructions.'); return $this->goHome(); } else { Yii::$app->session->setFlash('error', 'Sorry, we are unable to reset password for email provided.'); } } return $this->render('requestPasswordResetToken', [ 'model' => $model, ]); } /** * Resets password. * * @param string $token * @return mixed * @throws BadRequestHttpException */ public function actionResetPassword($token) { try { $model = new ResetPasswordForm($token); } catch (InvalidParamException $e) { throw new BadRequestHttpException($e->getMessage()); } if ($model->load(Yii::$app->request->post()) && $model->validate() && $model->resetPassword()) { Yii::$app->session->setFlash('success', 'New password was saved.'); return $this->goHome(); } return $this->render('resetPassword', [ 'model' => $model, } }
Далее для этих двух действий необходимо добавить представления, начнем с формы запроса на смену пароля /views/site/passwordResetRequestForm.php:
<?php use yii\helpers\Html; use yii\bootstrap\ActiveForm; $this->title = 'Request password reset'; $this->params['breadcrumbs'][] = $this->title; ?> <div class="site-request-password-reset"> <h1><?= Html::encode($this->title) ?></h1> <p>Please fill out your email. A link to reset password will be sent there.</p> <div class="row"> <div class="col-lg-5"> <?php $form = ActiveForm::begin(['id' => 'request-password-reset-form']); ?> <?= $form->field($model, 'email')->textInput(['autofocus' => true]) ?> <div class="form-group"> <?= Html::submitButton('Send', ['class' => 'btn btn-primary']) ?> </div> <?php ActiveForm::end(); ?> </div> </div> </div>
И представление для формы смены пароля /views/site/resetPasswordForm.php:
<?php use yii\helpers\Html; use yii\bootstrap\ActiveForm; $this->title = 'Reset password'; $this->params['breadcrumbs'][] = $this->title; ?> <div class="site-reset-password"> <h1><?= Html::encode($this->title) ?></h1> <p>Please choose your new password:</p> <div class="row"> <div class="col-lg-5"> <?php $form = ActiveForm::begin(['id' => 'reset-password-form']); ?> <?= $form->field($model, 'password')->passwordInput(['autofocus' => true]) ?> <div class="form-group"> <?= Html::submitButton('Save', ['class' => 'btn btn-primary']) ?> </div> <?php ActiveForm::end(); ?> </div> </div> </div>
Последний штрих, добавим ссылку на сброс пароля в представление формы авторизации, для этого открываем файл /views/site/login.php добавляем ссылку для сброса пароля:
<div> If you forgot your password you can <?= Html::a('reset it', ['site/request-password-reset']) ?>. </div>
В итоге представление авторизации на сайте будет выглядеть следующим образом:
<?php use yii\helpers\Html; use yii\bootstrap\ActiveForm; $this->title = 'Login'; $this->params['breadcrumbs'][] = $this->title; ?> <div class="site-login"> <h1><?= Html::encode($this->title) ?></h1> <p>Please fill out the following fields to login:</p> <?php $form = ActiveForm::begin([ 'id' => 'login-form', 'layout' => 'horizontal', 'fieldConfig' => [ 'template' => "{label}\n<div class=\"col-lg-3\">{input}</div>\n<div class=\"col-lg-8\">{error}</div>", 'labelOptions' => ['class' => 'col-lg-1 control-label'], ], ]); ?> <?= $form->field($model, 'username')->textInput(['autofocus' => true]) ?> <?= $form->field($model, 'password')->passwordInput() ?> <?= $form->field($model, 'rememberMe')->checkbox([ 'template' => "<div class=\"col-lg-offset-1 col-lg-3\">{input} {label}</div>\n<div class=\"col-lg-8\">{error}</div>", ]) ?> <div style="color:#999;margin:1em 0"> If you forgot your password you can <?= Html::a('reset it', ['site/request-password-reset']) ?>. </div> <div class="form-group"> <div class="col-lg-offset-1 col-lg-11"> <?= Html::submitButton('Login', ['class' => 'btn btn-primary', 'name' => 'login-button']) ?> </div> </div> <?php ActiveForm::end(); ?> </div>
В итоге мы получили полноценный функционал авторизации через БД, регистрации и сброса пароля в Yii2 basic используя функционал шаблона Yii2 advanced.
Авторизация в Yii2 по e-mail вместо username
Для авторизации на сайте мне больше нравится использование e-mail пользователя в качестве логина вместо ника, по умолчанию в yii2 используется ник в качестве логина. Исправить это довольно просто.
Начнем с модели /models/User.php, в ней необходимо добавить метод findByEmail для поиска пользователя по e-mail:
public static function findByEmail($email) { return static::findOne(['email' => $email, 'status' => self::STATUS_ACTIVE]); }
Далее в моделе /models/LoginForm.php меняем своойство (переменную) $username на $email в методе rules добавляем правило обработки e-mail [['email'], 'email'] и в методе getUser() меняем вызов User::findByUsername($this->username) на User::findByEmail($this->email), вот что получится:
<?php namespace app\models; use Yii; use yii\base\Model; /** * LoginForm is the model behind the login form. * * @property User|null $user This property is read-only. * */ class LoginForm extends Model { public $email; public $password; public $rememberMe = true; private $_user = false; /** * @return array the validation rules. */ public function rules() { return [ // username and password are both required [['email', 'password'], 'required'], [['email'], 'email'], // rememberMe must be a boolean value ['rememberMe', 'boolean'], // password is validated by validatePassword() ['password', 'validatePassword'], ]; } /** * Validates the password. * This method serves as the inline validation for password. * * @param string $attribute the attribute currently being validated * @param array $params the additional name-value pairs given in the rule */ public function validatePassword($attribute, $params) { if (!$this->hasErrors()) { $user = $this->getUser(); if (!$user || !$user->validatePassword($this->password)) { $this->addError($attribute, 'Incorrect username or password.'); } } } /** * Logs in a user using the provided username and password. * @return bool whether the user is logged in successfully */ public function login() { if ($this->validate()) { return Yii::$app->user->login($this->getUser(), $this->rememberMe ? 3600*24*30 : 0); } return false; } /** * Finds user by [[email]] * * @return User|null */ public function getUser() { if ($this->_user === false) { $this->_user = User::findByEmail($this->email); } return $this->_user; } }
И последнее, в представлении /views/site/login.php меняем поле username на email:
<?= $form->field($model, 'email')->textInput(['autofocus' => true]) ?>
Весь исходный код доступен на github: https://github.com/egor/yii2-basic-auth-through-db
Комментарии
Авторизируемся по username или email.
ОтветитьНемного меняем функцию findByEmail
Егор, у вас ошибка в разделе "Добавляем восстановление пароля на сайте в Yii2 basic". В настройках SiteController.php в экшанах надо прописать
соответственно.
Об остальном могу сказать одно: это один из лучших гайдов по этой теме) Остальные модули и иностранные гайды устарели и не работают (лично в моем случае). А ваш заработал! Спасибо Вам огромное!Ответить
Да, точно надо заменить или переименовать вьюхи, спасибо исправлю
ОтветитьХочу выразить благодарность автору, действительно хорошая во всех качествах статья
ОтветитьЛучшая статья по теме регистрации/авторизации на Yii2. Все понятно и без заморочек
ОтветитьОчень рад, что пригодилось
ОтветитьПривіт. Проблема:
ОтветитьInvalid Parameter – yii\base\InvalidParamExceptionWrong password reset token.
Invalid Parameter – yii\base\InvalidParamExceptionWrong password reset token.
1. in C:\xampp\htdocs\curch\models\ResetPasswordForm.phpat line 39
30313233343536373839404142434445464748 if (empty($token) || !is_string($token)) { throw new InvalidParamException('Password reset token cannot be blank.'); }
$this->_user = UserIdent::findByPasswordResetToken($token); var_dump($this->_user);
if (!$this->_user) { throw new InvalidParamException('Wrong password reset token.'); }
parent::__construct($config); }
/** * @inheritdoc */ public function rules()
2. in C:\xampp\htdocs\curch\controllers\UserController.php at line 188– app\models\ResetPasswordForm::__construct('3DVWpAbz1qRVQwGcct_iPW=Tqk7KF1lR...')
182183184185186187188189190191192193194 * @return mixed * @throws BadRequestHttpException
*/
public function actionResetPassword($token) { // try { $model = new ResetPasswordForm($token); // } catch (InvalidParamException $e) { // throw new BadRequestHttpException($e->getMessage()); // }
if ($model->load(Yii::$app->request->post()) && $model->validate() && $model->resetPassword()) { Yii::$app->session->setFlash('success', 'New password was saved.');
3. app\controllers\UserController::actionResetPassword('3DVWpAbz1qRVQwGcct_iPW=Tqk7KF1lR...')
4. in C:\xampp\htdocs\curch\vendor\yiisoft\yii2\base\InlineAction.php at line 55– call_user_func_array([app\controllers\UserController, 'actionResetPassword'], ['3DVWpAbz1qRVQwGcct_iPW=Tqk7KF1lR...'])
5. in C:\xampp\htdocs\curch\vendor\yiisoft\yii2\base\Controller.php at line 154– yii\base\InlineAction::runWithParams(['token' => '3DVWpAbz1qRVQwGcct_iPW=Tqk7KF1lR...'])
6. in C:\xampp\htdocs\curch\vendor\yiisoft\yii2\base\Module.php at line 454– yii\base\Controller::runAction('reset-password', ['token' => '3DVWpAbz1qRVQwGcct_iPW=Tqk7KF1lR...'])
7. in C:\xampp\htdocs\curch\vendor\yiisoft\yii2\web\Application.php at line 100– yii\base\Module::runAction('user/reset-password', ['token' => '3DVWpAbz1qRVQwGcct_iPW=Tqk7KF1lR...'])
8. in C:\xampp\htdocs\curch\vendor\yiisoft\yii2\base\Application.php at line 375– yii\web\Application::handleRequest(yii\web\Request)
9. in C:\xampp\htdocs\curch\web\index.php at line 12– yii\base\Application::run()
6789101112 require(__DIR__ . '/../vendor/autoload.php');require(__DIR__ . '/../vendor/yiisoft/yii2/Yii.php');
$config = require(__DIR__ . '/../config/web.php');
(new yii\web\Application($config))->run();
$_GET = [ 'token' => '3DVWpAbz1qRVQwGcct_iPW=Tqk7KF1lRfDi_1484317888',];
$_COOKIE = [ '_csrf' => '24f3675abcc58d277899972e6ccab69404d6802894a54194938ab5c46db4a214a:2:{i:0;s:5:"_csrf";i:1;s:32:"VLfrMQdi07ydDhsCBm6PLVWhFaBJh8T4";}', 'PHPSESSID' => 'sqng1fni89b52hbsdgkci2h0h0', '_gat' => '1', '_ga' => 'GA1.1.1939233386.1479989658',];
Це відбувається, коли я переходжу по посиланню з листа. Все інше - реєстрація, авторизація працює. Не розумію чому об'єкт виходить null....
ОтветитьЯкщо зробити var_dump($token) в actionResetPassword($token) що віддасть?
ОтветитьЯкщо зробити так
і в екшині
То на виході отримаю
[table]NULLstring(46) "3DmAQhybfVaYIJ4_ffZ1fb=MdckXxtI8Err_1484318712"array(2) { ["token"]=> string(46) "3DmAQhybfVaYIJ4_ffZ1fb=MdckXxtI8Err_1484318712" ["model"]=> object(app\models\ResetPasswordForm)#59 (7) { ["password"]=> NULL ["_user":"app\models\ResetPasswordForm":private]=> NULL ["_errors":"yii\base\Model":private]=> NULL ["_validators":"yii\base\Model":private]=> NULL ["_scenario":"yii\base\Model":private]=> string(7) "default" ["_events":"yii\base\Component":private]=> array(0) { } ["_behaviors":"yii\base\Component":private]=> NULL }}[/table]
Ответить
Здається мені що проблема десь в UserIdent, який не з прикладу
Ответитьuser.passwordResetTokenExpire встановлений?
ОтветитьПоставив більний час, бо помітив, що відрізнявся часовий пояс,
Мило
Message-ID: <962d25d19ec1226e51722987b7b2b76a@localhost>
Date: Fri, 13 Jan 2017 17:26:09 +0100
Subject: Password reset for My Application
From: My Application robot <robot@devreadwrite.com>
To: kotsubam@mail.ru
MIME-Version: 1.0
Content-Type: multipart/alternative;
boundary="_=_swift_v4_1484324769_f948e5ce1e4278b0692f1a71a7c09ecb_=_"
--_=_swift_v4_1484324769_f948e5ce1e4278b0692f1a71a7c09ecb_=_
Content-Type: text/plain; charset=utf-8
Content-Transfer-Encoding: quoted-printable
=20
Hello tester,
Follow the link below to reset your password:
=20
http://localhost/curch/web/user/reset-password?token=3DmAQhybfVaYIJ4_ffZ1fb=
MdckXxtI8Err_1484318712
--_=_swift_v4_1484324769_f948e5ce1e4278b0692f1a71a7c09ecb_=_
Content-Type: text/html; charset=utf-8
Content-Transfer-Encoding: quoted-printable
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org=
/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns=3D"http://www.w3.org/1999/=
xhtml">
<head>
<meta http-equiv=3D"Content-Type" content=3D"text/ht=
ml; charset=3DUTF-8" />
<title></title>
</head>
<body>
=
=20
<div class=3D"password-reset">
<p>Hello tester,</p>
<p>Follow the link below to reset your password:</p>
<p><a href=3D"http://localhost/curch/web/user/reset-password?token=3DmA=
QhybfVaYIJ4_ffZ1fbMdckXxtI8Err_1484318712">http://localhost/curch/web/user/=
reset-password?token=3DmAQhybfVaYIJ4_ffZ1fbMdckXxtI8Err_1484318712</a></p>
</div> </body>
</html>
--_=_swift_v4_1484324769_f948e5ce1e4278b0692f1a71a7c09ecb_=_--
Ответить
перевіряв Юзер клас, але там аж нічого такого не має. Методи по відновленню пароля із Вашого прикладу. Єдине- замінені назви полів
ОтветитьПопробуйте в классе (и в БД) увеличить длину токена
Потому-что кажется мне что он больше 32 символов (3DmAQhybfVaYIJ4_ffZ1fbMdckXxtI8Err_1484318712)
Если я правильно понял то это
из инструкции
Что в базе? Такой же токен как и в письме?Ответить
Я не знаю як правильно, але я роюлю для двох полів одне і теж значення.
Омеження 'access_token'], 'string', 'max' => 255 поставив всюди 255 симовлів для всіх полів і в тому числі в бд. ОтветитьКоли йде реєстрація
ОтветитьТокен в листі 3Dgn2ieBLFUfVYPoixTt4r=P1_5-BWxcm5e_1484337672
ОтветитьВ базі даних
auth_key = 9eFo1Y1ffswD2PM7qu-p9wZrDOrFCG2c
access_token = gn2ieBLFUfVYPoixTt4rP1_5-BWxcm5e_1484337672
Навіть, якщо вручну прибирати в посиланню http://localhost/curch/web/user/reset-password?token=3Dgn2ieBLFUfVYPoixTt4r=
ОтветитьP1_5-BWxcm5e_1484337672 занчення 3Dб всеодно не працює
В дебагері видно тільки ось
Я ваш пароль спрячу на всякий случай)
ОтветитьТак мы будем долго пробовать, если есть желание то отправьте на почту egor@кодер.укр архив с проектом и полный дамп базы, попробую помочь, но ничего не обещаю.
Если сами разберетесь, то пишите сюда что да как.
Поддерживаю ;)
ОтветитьВсё верно пишет Микола. Есть проблема с восстановлением пароля. И у вас не может работать корректно. По коду всё верно. где проблема - непонятно.
ОтветитьПросто в письме дописывает в начало токена 3D , в остальном всё хорошо
Відправив
ОтветитьДякую усім за допомогу, а особливу автору. . Проблеми були банальні - в базі даних поле містило менше 255 символів, тому новий пароль просто не поміщався. Все інше як у статті.
ОтветитьХорошая статья, все понятно и работает с первого раза, спасибо за сэкономленное время
ОтветитьВсе работает. Самая адекватная статья по текущей теме
ОтветитьМатериал изложен более чем достойно. Сайт однозначно в закладки.
ОтветитьУ меня была ошибка при логине с включёным чекбоксом "Remember me"
в файле User.php.Вылечилось добавлением
Это просто ошибка в статье или я что-то не так делаю?Ответить
Скачал с репозитория, проверил, все работает без:
ОтветитьМне кажется, что данная в статье не хватает данной инструкции по развертыванию окончательного примера из статьи:
Создаем БД с именем local_yii2_site_com, логин root, пароль пустой, если нужно изменить название БД, логин или пароль, то создаем необходимые и перед командой yii migrate правим файл соединения с БД: /config/db.php
Далее открываем консоль и выполняем следующие команды:
Перезапускаем веб сервер.
Добавляем пользователя (логин: admin, пароль: admin): http://yii2-auth.loc/web/index.php?r=site/add-admin
Готово.Ответить
Добрый день!
вы генерируете исключения на инвалидные токены, всё классно, ошибки ловятся.В модели
Вопрос, что с ними делать-то в итоге? Если пользователь попытается повторно сбросить пароль по старому токену или подменит его в ссылке, то сайт покажет ошибку с описанием исключения.
Нам же нужно как-то исключение обработать и показать пользователю ошибку, мол, запроси сброс пароля заново.Ответить
Делайте на свое усмотрение, например отдавайте 404 ошибку или другую страницу со своим описанием, это пример который не претендует на идеальность)
ОтветитьВсе вроде сделал правильно, но при /site/request-password-reset кидает на главную а сендмейл ничего не шлет..
ОтветитьПроверьте спам, работу отправки писем с сервера или пример из репозитория
Ответитьпример из репозитория. в денвере в папке появляется файл -sess_7a4a.............................. с содержимым tmp __flash|a:1:{s:7:"success";i:-1;}success|s:42:"Check your email for further instructions."; и все... в папке !sendmail писем не появляется.
ОтветитьФайл config/web.php закоментируйте строку:
Получаем примерно следующее:
После чего письма должны появиться.
Вот пример письма:
Его лучше открывать с помощью любой почтовой программы для нормального внешнего видаОтветить
Можно заменить отправку со стандартной на smtp, тогда точно будет отправляться, есть даже пример как это сделать: Yii 2, отправка почты (сообщения) через SMTP
Ответитьтеперь при переходе по ссылке из письма для сброса пароля вылезло вот это:
Namespace declaration statement has to be the very first statement in the script
in G:\openserver\OpenServer\domains\yii.test\models\ResetPasswordForm.php at line 2
Ответить
Огромнейшее вас спасибо! Очень долго не мог сделать авторизацию на yii, но благодаря вашему примеру написал авторизацию через стим.
ОтветитьСтатья отличная все работает, но как получить на странице данные текущего пользователя?
ОтветитьТ.е. если допустим делать личные кабинеты, у каждого свои данные, человек авторизовался, а дальше? Писать его id и login в сессию и таскать везде? Как вообще используя данный тут в статье код можно это сделать на Yii2 ?
Решил проблема. Всегда когда начинаю тормозить задаю вопрос и через пару минут нахожу ответ :)
Может кто-то также как и я только начинает знакомство с этим замечательным фреймворком, и для таких же вот ответ:
вызываем где надо и получаем необходимые данные )Ответить
password reset token не должно автоматически добавляет в бд при регистрации пользователья?
ОтветитьПривет.
ОтветитьГде тут лайкнуть?
Спасибо огромное
Ваш комментарий лучше любого лайка
ОтветитьПосле ввода емэйла для смены пароля на ту же страницу /site/request-password-reset
ОтветитьВ БД для пользователя в поле password_reset_token какой то токен создается. Но не переход на страницу уведомления об успешном запросе ни письмо со ссылкой не приходит.
В runtime\debug\mail насыпало несколько .eml в которых содержатся те самые ожидаемые письма, но даже если взять ссылку из них и открыть в браузере - 404.
#908 - этот коммент пробовали?
ОтветитьВообще такой секции нет. Добавление блока mailer не помогло. Мне достался такой сайт, но письма он каким то образом отправляет. В YII новичек, даже не представляю куда копать.
Ответитьмне помогло
назамена
зы статья супер!
Ответить
Статья то что нужно давно искал, не понимаю почему из коробки это не внедрили сразу .
ОтветитьНо у меня возникла проблема с внедрением данной формы на все страницы сайта ( в сайт бар) чтобы человек не переходил на лишнее страницы а мог сразу с любой страницы за логиниться . Не могу понять вываливается такая ошибка "Call to a member function isAttributeRequired() on null"
Слишком мало информация для определения проблемы. Возможно здесь найдете ответ Yii Framework, AJAX авторизация во всплывающем (модальном) окне (это не Yii2, но принцип тот же)
ОтветитьСпасибо за совет почитаю, ту ошибку я исправил. не правильно объявил виджет. Теперь выводится на сайте как надо форма. Но при авторизации идет редерект на стартовую и авторизация не проходит . Код LoginFormWidget.php
сам views
ну и вызов виждета
Делал по анологии в данной статье Yii2, форма обратной связи во сплывающем (модальном) окне
Ответить
А просто авторизация работает? На странице, без всплывающего окна.
ОтветитьДа простоая авторизация работает
ОтветитьНу тогда надо понять проблема после/до или вовремя авторизации:
Ответитьвыдало ошибку
на var_dunp($model->getErrors); die();Ответитьvar_dump()
Ответитьеще больше ошибок "Getting unknown property: app\models\LoginForm::getErrors"
ОтветитьВот правильный вариант (вроде):
В любом случае $model->login() возвращает false, надо смотреть что ему там не нравиться Ответить
Есть смысл еще посмотреть что лежит в Yii::$app->request->post(), может там не все параметры передаются
ОтветитьПоследний код рабочий и выводи OK
ОтветитьЕсли это не поможет, то вариантов у меня больше нет:
ОтветитьЯ тоже уже не знаю с чего подступится к решению этой проблемы ( Но все равно спасибо вы единственный кто откликнулся на вопрос. Последний код выдает ошибку
ОтветитьВсе из-за спешки, меняем $this->refresh() на Yii::$app->controller->refresh():
И я только сейчас заметил? loginformWidget.php, должно быть так:
Ответить
так же ошибка
ОтветитьObject of class yii\web\Response could not be converted to string
Интересно, завтра попробую сделать авторизацию в модальном окне, напишу инструкцию. Вроде подводных камней быть не должно
ОтветитьТогда ждем, мне тоже интересно
Ответитьспасибо. Уже даже интересно в чем же моя ошибка. Я только начал изучать Yii2
ОтветитьВот: Yii2 (basic + advanced), форма авторизации во всплывающем (модальном) окне
ОтветитьЗдравствуйте, поставил сея дополнение, все отлично работает. Но вот нужно получить ид текущего авторизированного юзера. И никак не могу. В главном контроллере
Не катит.Заранее спасибо.Ответить
О боже, полтора часа ломал голову, пока не понял что не авторизирован!
Ответить¯\_(ツ)_/¯
Ответить"Откроем контроллер /controllers/SiteController.php и добавим экшен в конец контроллера который поможет нам добавить пользователя"
Ответитьчтобы экшен сработал надо добавить в шапку use "app\models\User;", хз у кого какая версия, но у меня этой записи в basic шаблоне не было.
в use добаляем созданные модели и 2 действия (actionRequestPasswordReset и actionResetPassword):
59-я строка:
закрыть "]);" надо бы
______________________
"для отмены токена сброса пароля, ее необходимо указать в файле /congif/params.php:"
опечатка - /config/params.phpОтветить
Далее для этих двух действий необходимо добавить представления, начнем с формы запроса на смену пароля /views/site/passwordResetRequestForm.php
Ответить....
И представление для формы смены пароля /views/site/resetPasswordForm.php
нет таких файлов )) на github в этой папке лежат requestPasswordResetToken.php и resetPassword.php соотв.
Раздел Добавляем восстановление пароля на сайте в Yii2 basic
ОтветитьМодель /models/PasswordResetRequestForm.php
Строки 45 - 47. Зачем нам проверять существование юзера, если у нас в валидации стоит проверка?
При попытке запустить site.com/web/index.php?r=site/login выдаёт ошибку:
ОтветитьCannot redeclare class app\models\LoginForm
Статья хорошая... Не подсккажите это регистрация на три роли (админ и зарегистрированный пользователь, все остальные Гость) или на две (Админ-Пользователь, все остальные Гость)?
Ответитьвозникла проблема на этапе восстановления пароля по токену. Токен, который генерируется и сохраняется в БД отличается от токена высылаемого в письме, соответственно при переходе возникает ошибка "Wrong password reset token."
В БД токен 2EWpxPBAjZV4Wcrdb2tfnZ6Kwg5KipqE_1534249101
В письме 3DfnWvNPOdPuRLQZRt-Upi-KzQJHqlwGjw_1534246579Ответить
Я так понял, что тут не ограничивается длина токена размером VARCHAR(255), как реализовать это ограничение?
ОтветитьРеализовал через a.binetsky@ya.ru generateRandomString($length = 10), токены, все равно не совпадают
ОтветитьСвел генерацию токена, вовсе до time(), все равно в письме 3Diy5AHC9Ex5b5oalcfucuNa5yFQm1yq20_1534253550
ОтветитьНет такого файла в папке views/site/requestPasswordResetToken.php. Откуда его взять?
ОтветитьПодтверждаю актуальность. До сброса пароля (я это не реализовывал) все работает. Автору респект за качественный материал.
ОтветитьАвтор, спасибо за статью! Помогла настроить авторизацию на сайте Yii2 через БД
ОтветитьC:\xampp\htdocs\yii\basic>yii migrateYii Migration Tool (based on Yii v2.0.29)Total 1 new migration to be applied: m191114_160710_create_user_tableApply the above migration? (yes|no) [no]:yes*** applying m191114_160710_create_user_tableException 'yii\base\InvalidConfigException' with message 'Failed to instantiatecomponent or class "m191114_160710_create_user_table".'in C:\xampp\htdocs\yii\basic\vendor\yiisoft\yii2\di\Container.php:449Stack trace:#0 C:\xampp\htdocs\yii\basic\vendor\yiisoft\yii2\di\Container.php(374): yii\di\Container->getDependencies('m191114_160710_...')#1 C:\xampp\htdocs\yii\basic\vendor\yiisoft\yii2\di\Container.php(159): yii\di\Container->build('m191114_160710_...', Array, Array)#2 C:\xampp\htdocs\yii\basic\vendor\yiisoft\yii2\BaseYii.php(351): yii\di\Container->get('m191114_160710_...', Array, Array)#3 C:\xampp\htdocs\yii\basic\vendor\yiisoft\yii2\console\controllers\MigrateController.php(202): yii\BaseYii::createObject(Array)#4 C:\xampp\htdocs\yii\basic\vendor\yiisoft\yii2\console\controllers\BaseMigrateController.php(723): yii\console\controllers\MigrateController->createMigration('m191114_160710_...')#5 C:\xampp\htdocs\yii\basic\vendor\yiisoft\yii2\console\controllers\BaseMigrateController.php(200): yii\console\controllers\BaseMigrateController->migrateUp('m191114_160710_...')#6 [internal function]: yii\console\controllers\BaseMigrateController->actionUp(0)#7 C:\xampp\htdocs\yii\basic\vendor\yiisoft\yii2\base\InlineAction.php(57): call_user_func_array(Array, Array)#8 C:\xampp\htdocs\yii\basic\vendor\yiisoft\yii2\base\Controller.php(157): yii\base\InlineAction->runWithParams(Array)#9 C:\xampp\htdocs\yii\basic\vendor\yiisoft\yii2\console\Controller.php(164): yii\base\Controller->runAction('', Array)#10 C:\xampp\htdocs\yii\basic\vendor\yiisoft\yii2\base\Module.php(528): yii\console\Controller->runAction('', Array)#11 C:\xampp\htdocs\yii\basic\vendor\yiisoft\yii2\console\Application.php(180):yii\base\Module->runAction('migrate', Array)#12 C:\xampp\htdocs\yii\basic\vendor\yiisoft\yii2\console\Application.php(147):yii\console\Application->runAction('migrate', Array)#13 C:\xampp\htdocs\yii\basic\vendor\yiisoft\yii2\base\Application.php(386): yii\console\Application->handleRequest(Object(yii\console\Request))#14 C:\xampp\htdocs\yii\basic\yii(20): yii\base\Application->run()#15 {main}//////////////////////////////
ОтветитьУ кого то были проблемы с миграцией?
У меня проблема: при восстановлении пароля письмо не приходит. На сервере почтовый ящик создан, при использовании чистого php (mail("mylogin@mail.ru", "subject", "message", "From: support@mydomain.tk", "-f support@mydomain.tk");) письмо отправить удаётся. В чем может быть проблема?
ОтветитьРазобрался. В файле config/web.php необходимо прописать:
ОтветитьПри восстановлении пароля выдает ошибку:Wrong password reset token.
ОтветитьПопробуйте изменить password_reset_token с 255 на текст в БД, в модели тоже нужно убрать ограничение
ОтветитьСпасибо за мануал, наконец то руки дошли до авторизации. В миграцию добавил пользователя по умолчанию
Ответить