Пытаюсь сделать аутентификацию пользователей из БД. Как образец использовал пример http://code-epicenter.com/how-to-login-user-from-a-database-in-yii-framework-2/. !Другие примеры в интеренете сходны Столкнуля с тем что в моей медели в методе validatePassword() - $this->password === null. Также === null остальные аттрибути обьекта this. Но при этом self::getAttribute("password") возвращает пароль из БД. Также через self::getAttribute доступны другие аттрибути. Подскажите кто знает или догадивается - из-за чего глюк и как исправить ошибку. Моя модель models/Users.php PHP: <?php namespace app\models; use Yii; use yii\web\IdentityInterface; class Users extends \yii\db\ActiveRecord implements \yii\web\IdentityInterface { public $id; public $firstName; public $lastName; public $username; public $password; public $authKey; public $accessToken; public $role; /** * @inheritdoc */ public static function tableName() { return 'users'; } /** * @inheritdoc */ public function rules() { return [ [['username', 'password', 'authKey', 'accessToken', 'role'], 'required'], [['role'], 'integer'], [['firstName', 'lastName', 'username', 'password'], 'string', 'max' => 30], [['authKey', 'accessToken'], 'string', 'max' => 50] ]; } /** * @inheritdoc */ public function attributeLabels() { return [ 'id' => 'ID', 'firstName' => 'First Name', 'lastName' => 'Last Name', 'username' => 'Username', 'password' => 'Password', 'authKey' => 'Auth Key', 'accessToken' => 'Access Token', 'role' => 'Role', ]; } public static function findIdentity($id) { return self::findOne($id); } public static function findIdentityByAccessToken($token, $type = null) { throw new NotSupportedException(""); } public function getId() { return $this->id; } public function getAuthKey() { return $this->authKey; } public function validateAuthKey($authKey) { return $this->authKey === $authKey; } public static function findByUsername($username) return self::findOne(['username' => $username]); } public function validatePassword($password) { ///не работает потому что $this->password === null //return $this->password === $password; / //но так работает return $password === self::getAttribute("password"); } } скрипт - models/LoginForm.php - внесены изменения в метод getUser() PHP: <?php namespace app\models; use Yii; use yii\base\Model; /** * LoginForm is the model behind the login form. */ class LoginForm extends Model { public $username; public $password; public $rememberMe = true; private $_user = false; /** * @return array the validation rules. */ public function rules() { return [ // username and password are both required [['username', 'password'], 'required'], // 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 boolean 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 [[username]] * * @return User|null */ public function getUser() { if ($this->_user === false) { $this->_user = Users::findByUsername($this->username); } return $this->_user; } Скрипт config/web.php - PHP: <?php $params = require(__DIR__ . '/params.php'); $config = [ 'id' => 'basic', 'basePath' => dirname(__DIR__), 'bootstrap' => ['log'], 'components' => [ 'request' => [ // !!! insert a secret key in the following (if it is empty) - this is required by cookie validation 'cookieValidationKey' => 'login', ], 'cache' => [ 'class' => 'yii\caching\FileCache', ], 'user' => [ 'identityClass' => 'app\models\Users', 'enableAutoLogin' => false, ], 'errorHandler' => [ 'errorAction' => 'site/error', ], 'mailer' => [ 'class' => 'yii\swiftmailer\Mailer', // send all mails to a file by default. You have to set // 'useFileTransport' to false and configure a transport // for the mailer to send real emails. 'useFileTransport' => true, ], 'log' => [ 'traceLevel' => YII_DEBUG ? 3 : 0, 'targets' => [ [ 'class' => 'yii\log\FileTarget', 'levels' => ['error', 'warning'], ], ], ], 'db' => require(__DIR__ . '/db.php'), /* 'urlManager' => [ 'enablePrettyUrl' => true, 'showScriptName' => false, 'rules' => [ ], ], */ ], 'params' => $params, ]; if (YII_ENV_DEV) { // configuration adjustments for 'dev' environment $config['bootstrap'][] = 'debug'; $config['modules']['debug'] = [ 'class' => 'yii\debug\Module', ]; $config['bootstrap'][] = 'gii'; $config['modules']['gii'] = [ 'class' => 'yii\gii\Module', ]; } return $config;
Сделай dump объекта Users Немного не по теме, пароль же должен хешироваться PHP: public function validatePassword($password) { return Yii::$app->getSecurity()->validatePassword($password, $this->password); }
1. для $this все аттрибуты = null !хеширование это правило хорошего тона и тут все равно $this->password не должен быть null 2. Скрипт БД (вместо дампа) Код (Text): set names utf8; drop table if exists users; CREATE TABLE users( id int NOT NULL AUTO_INCREMENT PRIMARY KEY, firstName varchar(30), lastName varchar(30), username varchar(30) NOT NULL, password varchar(30) NOT NULL, authKey varchar(50) NOT NULL comment 'Auth key column required for cookie based login. Should be unique for every user', accessToken varchar(50) NOT NULL, role int NOT NULL ); insert into users(firstName, lastName, username, password, authKey, accessToken, role) values('Олександр', 'Шинк...','alex','alex','key1','token1',1);
action в которой ты заполняешь модель данными из POST и передаёшь в view --- Добавлено --- action в controller
Я использовал стандартную форму аутентификации views/site/login.php которая вызывается из котроллера SiteController.php методом public function actionLogin(). Ни в форму ни в контроллер никаких изменений не вносил - на тестовом сайте на котором пытаюсь реализовать аутентификацию из БД. На всякий случай привожу оба скрипта views/site/login.php PHP: <?php /* @var $this yii\web\View */ /* @var $form yii\bootstrap\ActiveForm */ /* @var $model app\models\LoginForm */ 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', 'options' => ['class' => 'form-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 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 class="col-lg-offset-1" style="color:#999;"> You may login with <strong>admin/admin</strong> or <strong>demo/demo</strong>.<br> To modify the username/password, please check out the code <code>app\models\User::$users</code>. </div> </div> SiteController.php PHP: <?php namespace app\controllers; use Yii; use yii\filters\AccessControl; use yii\web\Controller; use yii\filters\VerbFilter; use app\models\LoginForm; use app\models\ContactForm; class SiteController extends Controller { public function behaviors() { return [ 'access' => [ 'class' => AccessControl::className(), 'only' => ['logout'], 'rules' => [ [ 'actions' => ['logout'], 'allow' => true, 'roles' => ['@'], ], ], ], 'verbs' => [ 'class' => VerbFilter::className(), 'actions' => [ 'logout' => ['post'], ], ], ]; } public function actions() { return [ 'error' => [ 'class' => 'yii\web\ErrorAction', ], 'captcha' => [ 'class' => 'yii\captcha\CaptchaAction', 'fixedVerifyCode' => YII_ENV_TEST ? 'testme' : null, ], ]; } public function actionIndex() { return $this->render('index'); } public function actionLogin() { if (!\Yii::$app->user->isGuest) { return $this->goHome(); } $model = new LoginForm(); if ($model->load(Yii::$app->request->post()) && $model->login()) { return $this->goBack(); } return $this->render('login', [ 'model' => $model, ]); } public function actionLogout() { Yii::$app->user->logout(); return $this->goHome(); } public function actionContact() { $model = new ContactForm(); if ($model->load(Yii::$app->request->post()) && $model->contact(Yii::$app->params['adminEmail'])) { Yii::$app->session->setFlash('contactFormSubmitted'); return $this->refresh(); } return $this->render('contact', [ 'model' => $model, ]); } public function actionAbout() { return $this->render('about'); } }
В общем проблема была связана с декларацией свойств, а именно - блоком PHP: public $id; public $firstName; public $lastName; public $username; public $password; public $authKey; public $accessToken; public $role; После удаления этого блока проверка введенного пароля заработала правильно. - странно конечно, до сих пор не могу понять как декларация свойств в модуле приводила к тому что в объекте $this перечисленные свойства были не доступны. В оригинальном примере(указан в начале) этого блока деклараций разумеется не было Но теперь появилась новая проблема - после логина остается активной ссылка login в то время как в место нее должна появиться новая logout(user_name). Вход в систему происходит как то не до конца.
Ну всё правильно, ты объявил свойства. А где метод который заполняет их значениями? Ну и пытался получить значения пустых свойств.
Всё тут, но load не заполнял если свойства объявлены, он же их список из базы получает, возможно в validation rules можно было бы их пометить как safe. Но объявлять свойства там принято те, которых нет в таблице PHP: ($model->load(Yii::$app->request->post()) --- Добавлено --- @shurik_shink ставь лайки
@denis01 свойства определяются в классе ActiveRecord. Там же есть сеттер. PHP: class Users extends \yii\db\ActiveRecord Он переопределил свойства, разве они должны быть заполнены сеттером родительского класса?
Я сам не понял что написал. =) Только недавно начал изучать ООП, путаюсь в понятиях, поэтому не получается правильно сформулировать мысли. Со временем сам проверю свои домыслы.
@denis01, проблема с объявленными свойствами была в том, что когда они объявлены напрямую, то не работают магические методы из ActiveRecord, а php отдаёт эти свойства. ActiveRecord хранит все поля, загруженные из базы, в массиве $_attributes, и через магию это дело отдаёт. В общем, когда используется ActiveRecord, не надо дублировать объявление полей из базы полями класса, там всё должно работать через магию ActiveRecord (__get(), __set())
@Denis, ага. Только load() и rules() не причём. Они для другого. populateRecord() устанавливает поля класса в соответствии с полями, полученными из базы. Вызывается скрыто
Как не причём load() если он его использует? Я ему объясняю, что если он объявит свойства, то они не будут заполняться, потому что механизм там другой. Про rules() это если через load() он заполняет и хочет заполнить объявленные свойства в классе которых нет в базе. Ты дополнил объяснение внутреннего механизма.
load-ом он заполняет LoginForm, а не User, передавая в него заполненные пользователем поля. LoginFrom не ActiveRecord, и там у него вроде как всё в порядке.