За последние 24 часа нас посетили 20776 программистов и 1133 робота. Сейчас ищут 406 программистов ...

Проблема при аутентификации из БД, $this->password === null

Тема в разделе "Yii", создана пользователем shurik_shink, 12 сен 2016.

  1. shurik_shink

    shurik_shink Новичок

    С нами с:
    17 май 2015
    Сообщения:
    79
    Симпатии:
    0
    Пытаюсь сделать аутентификацию пользователей из БД. Как образец использовал пример 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:
    1. <?php
    2.  
    3. namespace app\models;
    4.  
    5. use Yii;
    6.  
    7. use yii\web\IdentityInterface;
    8.  
    9.  
    10. class Users extends \yii\db\ActiveRecord implements \yii\web\IdentityInterface
    11. {
    12.     public $id;
    13.     public $firstName;
    14.     public $lastName;
    15.     public $username;
    16.     public $password;
    17.     public $authKey;
    18.     public $accessToken;
    19.     public $role;
    20.    
    21.     /**
    22.      * @inheritdoc
    23.      */
    24.     public static function tableName()
    25.     {
    26.         return 'users';
    27.     }
    28.  
    29.     /**
    30.      * @inheritdoc
    31.      */
    32.     public function rules()
    33.     {
    34.         return [
    35.             [['username', 'password', 'authKey', 'accessToken', 'role'], 'required'],
    36.             [['role'], 'integer'],
    37.             [['firstName', 'lastName', 'username', 'password'], 'string', 'max' => 30],
    38.             [['authKey', 'accessToken'], 'string', 'max' => 50]
    39.         ];
    40.     }
    41.  
    42.     /**
    43.      * @inheritdoc
    44.      */
    45.     public function attributeLabels()
    46.     {
    47.         return [
    48.             'id' => 'ID',
    49.             'firstName' => 'First Name',
    50.             'lastName' => 'Last Name',
    51.             'username' => 'Username',
    52.             'password' => 'Password',
    53.             'authKey' => 'Auth Key',
    54.             'accessToken' => 'Access Token',
    55.             'role' => 'Role',
    56.         ];
    57.     }
    58.    
    59.     public static function findIdentity($id)
    60.     {
    61.         return self::findOne($id);
    62.        
    63.     }
    64.    
    65.     public static function findIdentityByAccessToken($token, $type = null)
    66.     {
    67.         throw new NotSupportedException("");
    68.     }
    69.    
    70.     public function getId()
    71.     {
    72.         return $this->id;
    73.     }
    74.    
    75.     public function getAuthKey()
    76.     {
    77.         return $this->authKey;
    78.     }
    79.    
    80.     public function validateAuthKey($authKey)
    81.     {
    82.         return $this->authKey === $authKey;
    83.     }
    84.    
    85.     public static function findByUsername($username)
    86.         return self::findOne(['username' => $username]);
    87.        
    88.     }
    89.    
    90.     public function validatePassword($password)
    91.     {
    92.         ///не работает потому что $this->password === null
    93.         //return $this->password === $password;  /
    94.        
    95.        
    96.         //но так работает
    97.         return $password === self::getAttribute("password");
    98.     }  
    99.    
    100. }
    скрипт - models/LoginForm.php - внесены изменения в метод getUser()
    PHP:
    1. <?php
    2.  
    3. namespace app\models;
    4.  
    5. use Yii;
    6.  
    7. use yii\base\Model;
    8.  
    9. /**
    10. * LoginForm is the model behind the login form.
    11. */
    12. class LoginForm extends Model
    13. {
    14.     public $username;
    15.     public $password;
    16.     public $rememberMe = true;
    17.  
    18.     private $_user = false;
    19.  
    20.  
    21.     /**
    22.      * @return array the validation rules.
    23.      */
    24.     public function rules()
    25.     {
    26.         return [
    27.             // username and password are both required
    28.             [['username', 'password'], 'required'],
    29.             // rememberMe must be a boolean value
    30.             ['rememberMe', 'boolean'],
    31.             // password is validated by validatePassword()
    32.             ['password', 'validatePassword'],
    33.         ];
    34.     }
    35.  
    36.     /**
    37.      * Validates the password.
    38.      * This method serves as the inline validation for password.
    39.      *
    40.      * @param string $attribute the attribute currently being validated
    41.      * @param array $params the additional name-value pairs given in the rule
    42.      */
    43.     public function validatePassword($attribute, $params)
    44.     {
    45.         if (!$this->hasErrors()) {
    46.  
    47.             $user = $this->getUser();
    48.  
    49.             if (!$user || !$user->validatePassword($this->password)) {
    50.                 $this->addError($attribute, 'Incorrect username or password.');
    51.             }
    52.         }
    53.     }
    54.  
    55.     /**
    56.      * Logs in a user using the provided username and password.
    57.      * @return boolean whether the user is logged in successfully
    58.      */
    59.     public function login()
    60.     {
    61.         if ($this->validate()) {
    62.             return Yii::$app->user->login($this->getUser(), $this->rememberMe ? 3600*24*30 : 0);
    63.         }
    64.         return false;
    65.     }
    66.  
    67.     /**
    68.      * Finds user by [[username]]
    69.      *
    70.      * @return User|null
    71.      */
    72.     public function getUser()
    73.     {
    74.         if ($this->_user === false) {
    75.             $this->_user = Users::findByUsername($this->username);
    76.         }
    77.         return $this->_user;
    78.     }

    Скрипт config/web.php -
    PHP:
    1. <?php
    2.  
    3. $params = require(__DIR__ . '/params.php');
    4.  
    5. $config = [
    6.     'id' => 'basic',
    7.     'basePath' => dirname(__DIR__),
    8.     'bootstrap' => ['log'],
    9.     'components' => [
    10.         'request' => [
    11.             // !!! insert a secret key in the following (if it is empty) - this is required by cookie validation
    12.             'cookieValidationKey' => 'login',
    13.         ],
    14.         'cache' => [
    15.             'class' => 'yii\caching\FileCache',
    16.         ],
    17.         'user' => [
    18.             'identityClass' => 'app\models\Users',
    19.             'enableAutoLogin' => false,
    20.         ],
    21.         'errorHandler' => [
    22.             'errorAction' => 'site/error',
    23.         ],
    24.         'mailer' => [
    25.             'class' => 'yii\swiftmailer\Mailer',
    26.             // send all mails to a file by default. You have to set
    27.             // 'useFileTransport' to false and configure a transport
    28.             // for the mailer to send real emails.
    29.             'useFileTransport' => true,
    30.         ],
    31.         'log' => [
    32.             'traceLevel' => YII_DEBUG ? 3 : 0,
    33.             'targets' => [
    34.                 [
    35.                     'class' => 'yii\log\FileTarget',
    36.                     'levels' => ['error', 'warning'],
    37.                 ],
    38.             ],
    39.         ],
    40.         'db' => require(__DIR__ . '/db.php'),
    41.         /*
    42.         'urlManager' => [
    43.             'enablePrettyUrl' => true,
    44.             'showScriptName' => false,
    45.             'rules' => [
    46.             ],
    47.         ],
    48.         */
    49.     ],
    50.     'params' => $params,
    51. ];
    52.  
    53. if (YII_ENV_DEV) {
    54.     // configuration adjustments for 'dev' environment
    55.     $config['bootstrap'][] = 'debug';
    56.     $config['modules']['debug'] = [
    57.         'class' => 'yii\debug\Module',
    58.     ];
    59.  
    60.     $config['bootstrap'][] = 'gii';
    61.     $config['modules']['gii'] = [
    62.         'class' => 'yii\gii\Module',
    63.     ];
    64. }
    65.  
    66. return $config;
     
  2. denis01

    denis01 Суперстар
    Команда форума Модератор

    С нами с:
    9 дек 2014
    Сообщения:
    12.230
    Симпатии:
    1.715
    Адрес:
    Молдова, г.Кишинёв
    Сделай dump объекта Users

    Немного не по теме, пароль же должен хешироваться

    PHP:
    1.     public function validatePassword($password)
    2.     {
    3.        return Yii::$app->getSecurity()->validatePassword($password, $this->password);
    4.     }
     
  3. shurik_shink

    shurik_shink Новичок

    С нами с:
    17 май 2015
    Сообщения:
    79
    Симпатии:
    0
    1. для $this все аттрибуты = null
    !хеширование это правило хорошего тона и тут все равно $this->password не должен быть null
    2. Скрипт БД (вместо дампа)
    Код (Text):
    1. set names utf8;
    2.  
    3. drop table if exists users;
    4.  
    5. CREATE TABLE users(
    6.   id int NOT NULL AUTO_INCREMENT PRIMARY KEY,
    7.   firstName varchar(30),
    8.   lastName varchar(30),
    9.   username varchar(30) NOT NULL,
    10.   password varchar(30) NOT NULL,
    11.   authKey varchar(50) NOT NULL comment 'Auth key column required for cookie based login. Should be unique for every user',
    12.   accessToken varchar(50) NOT NULL,
    13.   role int NOT NULL
    14. );
    15.  
    16. insert into users(firstName, lastName, username, password, authKey, accessToken, role)
    17. values('Олександр', 'Шинк...','alex','alex','key1','token1',1);
     
  4. denis01

    denis01 Суперстар
    Команда форума Модератор

    С нами с:
    9 дек 2014
    Сообщения:
    12.230
    Симпатии:
    1.715
    Адрес:
    Молдова, г.Кишинёв
    Покажи action формы, может там мелочь закралась
     
  5. shurik_shink

    shurik_shink Новичок

    С нами с:
    17 май 2015
    Сообщения:
    79
    Симпатии:
    0
    какой формы? - если идет речь про форму views/site/login.php - то эту форму не менял - стандартная
     
  6. denis01

    denis01 Суперстар
    Команда форума Модератор

    С нами с:
    9 дек 2014
    Сообщения:
    12.230
    Симпатии:
    1.715
    Адрес:
    Молдова, г.Кишинёв
    action в которой ты заполняешь модель данными из POST и передаёшь в view
    --- Добавлено ---
    action в controller
     
  7. shurik_shink

    shurik_shink Новичок

    С нами с:
    17 май 2015
    Сообщения:
    79
    Симпатии:
    0
    Я использовал стандартную форму аутентификации views/site/login.php которая вызывается из котроллера SiteController.php методом public function actionLogin(). Ни в форму ни в контроллер никаких изменений не вносил - на тестовом сайте на котором пытаюсь реализовать аутентификацию из БД.

    На всякий случай привожу оба скрипта
    views/site/login.php
    PHP:
    1. <?php
    2.  
    3. /* @var $this yii\web\View */
    4. /* @var $form yii\bootstrap\ActiveForm */
    5. /* @var $model app\models\LoginForm */
    6.  
    7. use yii\helpers\Html;
    8. use yii\bootstrap\ActiveForm;
    9.  
    10. $this->title = 'Login';
    11. $this->params['breadcrumbs'][] = $this->title;
    12. ?>
    13. <div class="site-login">
    14.     <h1><?= Html::encode($this->title) ?></h1>
    15.  
    16.     <p>Please fill out the following fields to login:</p>
    17.  
    18.     <?php $form = ActiveForm::begin([
    19.         'id' => 'login-form',
    20.         'options' => ['class' => 'form-horizontal'],
    21.         'fieldConfig' => [
    22.             'template' => "{label}\n<div class=\"col-lg-3\">{input}</div>\n<div class=\"col-lg-8\">{error}</div>",
    23.             'labelOptions' => ['class' => 'col-lg-1 control-label'],
    24.         ],
    25.     ]); ?>
    26.  
    27.         <?= $form->field($model, 'username')->textInput(['autofocus' => true]) ?>
    28.  
    29.         <?= $form->field($model, 'password')->passwordInput() ?>
    30.  
    31.         <?= $form->field($model, 'rememberMe')->checkbox([
    32.             'template' => "<div class=\"col-lg-offset-1 col-lg-3\">{input} {label}</div>\n<div class=\"col-lg-8\">{error}</div>",
    33.         ]) ?>
    34.  
    35.         <div class="form-group">
    36.             <div class="col-lg-offset-1 col-lg-11">
    37.                 <?= Html::submitButton('Login', ['class' => 'btn btn-primary', 'name' => 'login-button']) ?>
    38.             </div>
    39.         </div>
    40.  
    41.     <?php ActiveForm::end(); ?>
    42.  
    43.     <div class="col-lg-offset-1" style="color:#999;">
    44.         You may login with <strong>admin/admin</strong> or <strong>demo/demo</strong>.<br>
    45.         To modify the username/password, please check out the code <code>app\models\User::$users</code>.
    46.     </div>
    47. </div>
    SiteController.php
    PHP:
    1. <?php
    2.  
    3. namespace app\controllers;
    4.  
    5. use Yii;
    6. use yii\filters\AccessControl;
    7. use yii\web\Controller;
    8. use yii\filters\VerbFilter;
    9. use app\models\LoginForm;
    10. use app\models\ContactForm;
    11.  
    12. class SiteController extends Controller
    13. {
    14.     public function behaviors()
    15.     {
    16.         return [
    17.             'access' => [
    18.                 'class' => AccessControl::className(),
    19.                 'only' => ['logout'],
    20.                 'rules' => [
    21.                     [
    22.                         'actions' => ['logout'],
    23.                         'allow' => true,
    24.                         'roles' => ['@'],
    25.                     ],
    26.                 ],
    27.             ],
    28.             'verbs' => [
    29.                 'class' => VerbFilter::className(),
    30.                 'actions' => [
    31.                     'logout' => ['post'],
    32.                 ],
    33.             ],
    34.         ];
    35.     }
    36.  
    37.     public function actions()
    38.     {
    39.         return [
    40.             'error' => [
    41.                 'class' => 'yii\web\ErrorAction',
    42.             ],
    43.             'captcha' => [
    44.                 'class' => 'yii\captcha\CaptchaAction',
    45.                 'fixedVerifyCode' => YII_ENV_TEST ? 'testme' : null,
    46.             ],
    47.         ];
    48.     }
    49.  
    50.     public function actionIndex()
    51.     {
    52.         return $this->render('index');
    53.     }
    54.  
    55.     public function actionLogin()
    56.     {
    57.         if (!\Yii::$app->user->isGuest) {
    58.             return $this->goHome();
    59.         }
    60.  
    61.         $model = new LoginForm();
    62.         if ($model->load(Yii::$app->request->post()) && $model->login()) {
    63.             return $this->goBack();
    64.         }
    65.         return $this->render('login', [
    66.             'model' => $model,
    67.         ]);
    68.     }
    69.  
    70.     public function actionLogout()
    71.     {
    72.         Yii::$app->user->logout();
    73.  
    74.         return $this->goHome();
    75.     }
    76.  
    77.     public function actionContact()
    78.     {
    79.         $model = new ContactForm();
    80.         if ($model->load(Yii::$app->request->post()) && $model->contact(Yii::$app->params['adminEmail'])) {
    81.             Yii::$app->session->setFlash('contactFormSubmitted');
    82.  
    83.             return $this->refresh();
    84.         }
    85.         return $this->render('contact', [
    86.             'model' => $model,
    87.         ]);
    88.     }
    89.  
    90.     public function actionAbout()
    91.     {
    92.         return $this->render('about');
    93.     }
    94. }
     
  8. shurik_shink

    shurik_shink Новичок

    С нами с:
    17 май 2015
    Сообщения:
    79
    Симпатии:
    0
    В общем проблема была связана с декларацией свойств, а именно - блоком
    PHP:
    1.     public $id;
    2.     public $firstName;
    3.     public $lastName;
    4.     public $username;
    5.     public $password;
    6.     public $authKey;
    7.     public $accessToken;
    8.     public $role;
    После удаления этого блока проверка введенного пароля заработала правильно.
    - странно конечно, до сих пор не могу понять как декларация свойств в модуле приводила к тому что в объекте $this перечисленные свойства были не доступны. В оригинальном примере(указан в начале) этого блока деклараций разумеется не было

    Но теперь появилась новая проблема - после логина остается активной ссылка login в то время как в место нее должна появиться новая logout(user_name). Вход в систему происходит как то не до конца.
     
  9. denis01

    denis01 Суперстар
    Команда форума Модератор

    С нами с:
    9 дек 2014
    Сообщения:
    12.230
    Симпатии:
    1.715
    Адрес:
    Молдова, г.Кишинёв
    isGuest что показывает?
     
  10. shurik_shink

    shurik_shink Новичок

    С нами с:
    17 май 2015
    Сообщения:
    79
    Симпатии:
    0
    PHP:
    1. <?php echo "isGuest=".Yii::$app->user->isGuest."<br>"; ?>
    isGuest=1
     
  11. denis01

    denis01 Суперстар
    Команда форума Модератор

    С нами с:
    9 дек 2014
    Сообщения:
    12.230
    Симпатии:
    1.715
    Адрес:
    Молдова, г.Кишинёв
    Может ещё это вклчючить
     
    shurik_shink нравится это.
  12. bikerlex

    bikerlex Активный пользователь

    С нами с:
    2 дек 2014
    Сообщения:
    344
    Симпатии:
    40
    Ну всё правильно, ты объявил свойства. А где метод который заполняет их значениями? Ну и пытался получить значения пустых свойств.
     
  13. shurik_shink

    shurik_shink Новичок

    С нами с:
    17 май 2015
    Сообщения:
    79
    Симпатии:
    0
    Спасибо
    Установил
    PHP:
    1. 'enableAutoLogin' => true
    Теперь все работает - и login и logout
     
  14. denis01

    denis01 Суперстар
    Команда форума Модератор

    С нами с:
    9 дек 2014
    Сообщения:
    12.230
    Симпатии:
    1.715
    Адрес:
    Молдова, г.Кишинёв
    Всё тут, но load не заполнял если свойства объявлены, он же их список из базы получает, возможно в validation rules можно было бы их пометить как safe. Но объявлять свойства там принято те, которых нет в таблице
    PHP:
    1. ($model->load(Yii::$app->request->post())
    --- Добавлено ---
    @shurik_shink ставь лайки :rolleyes:
     
  15. bikerlex

    bikerlex Активный пользователь

    С нами с:
    2 дек 2014
    Сообщения:
    344
    Симпатии:
    40
    @denis01 свойства определяются в классе ActiveRecord. Там же есть сеттер.
    PHP:
    1. class Users extends \yii\db\ActiveRecord
    Он переопределил свойства, разве они должны быть заполнены сеттером родительского класса?
     
  16. denis01

    denis01 Суперстар
    Команда форума Модератор

    С нами с:
    9 дек 2014
    Сообщения:
    12.230
    Симпатии:
    1.715
    Адрес:
    Молдова, г.Кишинёв
    не понял
     
  17. bikerlex

    bikerlex Активный пользователь

    С нами с:
    2 дек 2014
    Сообщения:
    344
    Симпатии:
    40
    Я сам не понял что написал. =) Только недавно начал изучать ООП, путаюсь в понятиях, поэтому не получается правильно сформулировать мысли. Со временем сам проверю свои домыслы.
     
    denis01 нравится это.
  18. mkramer

    mkramer Суперстар
    Команда форума Модератор

    С нами с:
    20 июн 2012
    Сообщения:
    8.548
    Симпатии:
    1.754
    @denis01, проблема с объявленными свойствами была в том, что когда они объявлены напрямую, то не работают магические методы из ActiveRecord, а php отдаёт эти свойства. ActiveRecord хранит все поля, загруженные из базы, в массиве $_attributes, и через магию это дело отдаёт. В общем, когда используется ActiveRecord, не надо дублировать объявление полей из базы полями класса, там всё должно работать через магию ActiveRecord (__get(), __set())
     
    denis01 нравится это.
  19. denis01

    denis01 Суперстар
    Команда форума Модератор

    С нами с:
    9 дек 2014
    Сообщения:
    12.230
    Симпатии:
    1.715
    Адрес:
    Молдова, г.Кишинёв
    Всё верно, говорим, об одном и том же.
     
  20. mkramer

    mkramer Суперстар
    Команда форума Модератор

    С нами с:
    20 июн 2012
    Сообщения:
    8.548
    Симпатии:
    1.754
    @Denis, ага. Только load() и rules() не причём. Они для другого. populateRecord() устанавливает поля класса в соответствии с полями, полученными из базы. Вызывается скрыто
     
  21. denis01

    denis01 Суперстар
    Команда форума Модератор

    С нами с:
    9 дек 2014
    Сообщения:
    12.230
    Симпатии:
    1.715
    Адрес:
    Молдова, г.Кишинёв
    Как не причём load() если он его использует? Я ему объясняю, что если он объявит свойства, то они не будут заполняться, потому что механизм там другой.
    Про rules() это если через load() он заполняет и хочет заполнить объявленные свойства в классе которых нет в базе.

    Ты дополнил объяснение внутреннего механизма.
     
  22. mkramer

    mkramer Суперстар
    Команда форума Модератор

    С нами с:
    20 июн 2012
    Сообщения:
    8.548
    Симпатии:
    1.754
    load-ом он заполняет LoginForm, а не User, передавая в него заполненные пользователем поля. LoginFrom не ActiveRecord, и там у него вроде как всё в порядке.
     
  23. denis01

    denis01 Суперстар
    Команда форума Модератор

    С нами с:
    9 дек 2014
    Сообщения:
    12.230
    Симпатии:
    1.715
    Адрес:
    Молдова, г.Кишинёв
    Да, точно, мысль я не самым понятным образом высказал