Курс по Yii с нуля. Урок 3. Создаём новых пользователей. Организуем простую систему авторизации. Аутентификация, пароли, соли.

Курс по Yii с нуля. Урок 3. Создаём новых пользователей. Организуем простую систему авторизации. Аутентификация, пароли, соли. Как создать других пользователей, кроме demo в стандартном блоге. Сделать себя администратором.

В Yii блоге, который мы установили по умолчанию, и настроили в 2-х предыдущих уроках, уже модель User (связана с таблицей tbl_user), но ни контроллера, ни файлов видов ещё не создано, а в базе - только один бедняжка "demo". Есть что развивать.

1) С помощью Gii создадим для модели User свою Crud. (Так за секунды мы получим контроллер с необходимыми функциями работы с пользователями + все файлы видов).

Если у вас пока не включен Gii, то надо включить его в работу, прописав в config/main.php:

'modules'=>array(
		// uncomment the following to enable the Gii tool
		
		'gii'=>array(
			'class'=>'system.gii.GiiModule',
			'password'=>'yourpassword',
		 	// If removed, Gii defaults to localhost only. Edit carefully to taste.
			'ipFilters'=>array('127.0.0.1', '::1'),
		),
	),

2) При попытке открыть /index.php/user, выпадает ошибка 500 (говорит, что нет search в модели).

Не проблема. Возьмём этот метод из модели Post. И изменим, что надо и добавим в models/User.php:

public function search()
	{
		$criteria=new CDbCriteria;
		$criteria->compare('username',$this->username,true);
		$criteria->compare('email',$this->email);

		return new CActiveDataProvider('User', array(
			'criteria'=>$criteria,
			'sort'=>array(
				'defaultOrder'=>'username, email DESC',
			),
		));
	}

3) Теперь закомментируем временно accessRules() в контроллер UserController или разрешим demo функции администраторские (потому что этот demo пока у нас один и мы на него возлагаем всю работу).

Создаём от его лица нового пользователя 'admin' (через путь 'index.php/user/create'). Видим что у нас просят зачем-то Salt (надо чтобы сама генерировалась), и кроме того, пароль не кодируется, а сохраняется в базу как мы его ввели. Это надо доработать.

4) В User::rules() дописываем проверку на уникальность email и username:

public function rules()
{
// NOTE: you should only define rules for those attributes that // will receive user inputs. return array(
...
array('email, username', 'unique'),
// The following rule is used by search(). 
// Please remove those attributes that should not be searched. 
array('id, email, username, password', 'safe', 'on'=>'search'),
);
}

5) Удалим из protected/views/user/_form поле 'salt' и добавим поле для повтора пароля

<div class="row"> 
<?php echo $form->label($model,'password_repeat'); ?> 
<?php echo $form->passwordField($model,'password_repeat',array('size'=>60,'maxlength'=>256)); ?> 
<?php echo $form->error($model,'password_repeat'); ?>
</div>

Это обычная практика, когда нового пользователя при регистрации просят ввести пароль повторно, чтобы он не ошибся. Встроенный в Yii класс CCompareValidator поможет нам сравнить пароли в двух полях.

Добавляем в модель User в самый верх переменную $password_repeat:

<?php
class User extends CActiveRecord
{
	public $password_repeat;
...

Мы добавляем в названии арибута _repeat, потому что валидатор так работает. Если он не находит явно указанного поля, с которым производить сравнение, то сравнит с полем такого же названия с добавкой _repeat.

Теперь добавим в User::rules() правило compare, и пропишем password_repeat в безопасные атрибуты, чтобы он мог передаваться при вызове setAttributes(), хотя он и не является полем в таблице tbl_user:

array('password', 'compare'),
array('profile, password_repeat', 'safe'),

6) Вернёмся к закодированию пароля при добавлении в базу данных. Сейчас он не кодируется никак и это небезопасно. Но и тут всё относительно просто: нам поможет метод afterValidate(). В модели Users уже нам разработчики заготовили функции для шифрования, можно написать свои, но я пока оставляю как есть, только добавляю afterValidate():

// После валидации присваиваем пароль и соль.
  public function afterValidate()
  {
 				$this->password = self::hashPassword($this->password, $this->salt);
        if($this->isNewRecord) {
            $salt = self::generateSalt();
            $this->password = self::hashPassword($this->password, $salt);
            $this->salt = $salt;
            //$this->role = 'user';
        }
        return true;
  }

	// Проверка валидности пароля
	public function validatePassword($password)
	{
		return $this->hashPassword($password,$this->salt)===$this->password;
	}

	// Создание хэша пароля
	public function hashPassword($password,$salt)
	{
		return md5($salt.$password);
	}

	/**
	 * Generates a salt that can be used to generate a password hash.
	 * @return string the salt
	 */
	protected function generateSalt()
	{
		return uniqid('',true);
	}

ВНИМАНИЕ: вместо afterValidate() лучше использовать beforeSave(), в след. уроке мы делаем эту замену, здесь для первого общего понимания оставим afterValidate().

Тут соль генерируется как это сделано по-умолчанию, с помощью функции uniqid(). Но функция генерации соли может быть например, как предлагает timlar на форуме yiiframework.ru

// Генерация "соли". Этот метод генерирует случайным образом слово
    // заданной длины. Длина указывается в единственном свойстве метода.
    // Символы, применяемые при генерации, указаны в переменной $chars.
    // По умолчанию, длина соли 32 символа.
    public function generateSalt($length=32)
    {
        $chars = "abcdefghijkmnopqrstuvwxyz023456789";
        srand((double)microtime()*1000000);
        $i = 1;
        $salt = '' ;

        while ($i <= $length)
        {
            $num = rand() % 33;
            $tmp = substr($chars, $num, 1);
            $salt .= $tmp;
            $i++;
        }
        return $salt;
    } 

Ещё мы немного изменили для кратости метод authenticate в компоненте UserIdentity, посмотрите:

public function authenticate()
	{
		$user=User::model()->find('LOWER(username)=?',array(strtolower($this->username)));
		if($user===null)
			$this->errorCode=self::ERROR_USERNAME_INVALID;
		else if(!$user->validatePassword($this->password))
			$this->errorCode=self::ERROR_PASSWORD_INVALID;
		else
		{
			$this->_id=$user->id;
			$this->username=$user->username;
			$this->errorCode=self::ERROR_NONE;
		}
		return !$this->errorCode;
	}

Цель достигнута, не забудьте восстановить accessRules(), и указать там в правах нужных пользователей. Мы не вводили роли пользователям, пока по простому, для больших приложений удобнее будет взять расширения (например yii-users, yii-user-management).

И добавьте в шаблон, например в column2.php

<?php
			$this->beginWidget('zii.widgets.CPortlet', array(
				'title'=>'Operations',
			));
			$this->widget('zii.widgets.CMenu', array(
				'items'=>$this->menu,
				'htmlOptions'=>array('class'=>'operations'),
			));
			$this->endWidget();
		?>

Это покажет меню, созданные Gii. А в общее меню (компонент userMenu.php) добавим ссылку на управление пользователями:

<li><?php echo CHtml::link('Manage Users',array('user/admin'), $htmlOptions=array('class'=>'admin')); ?></li>

До следующего урока, друзья!

Источник: loco.ru

almix
Разработчик Loco, автор статей по веб-разработке на Yii, CodeIgniter, MODx и прочих инструментах. Создатель Team Sense.

Вы можете почитать все статьи от almix'а.



Другие статьи по этой теме:
  1. 14 Разработка приложения на Yii. Урок 14: Выводим список категорий как новый виджет на сайте (29.04.2015) free
  2. 13 Разработка приложения на Yii. Урок 13: Внедряем категории для статей. (29.04.2015) free
  3. 12 Разработка приложения на Yii. Урок 12: Профилирование приложения, включаем кеширование. (20.01.2015)
  4. 11 Yiico. Видеокурс по разработке сайта на Yii. Урок 11: Отладка приложения, включаем журналирование. (20.01.2015)
  5. 10 Yiico. Видеокурс по разработке сайта на Yii. Урок 10: Если ваше приложение находится не в корневой папке, а во вложенной. (19.10.2014) free
  6. 9 Разработка сайта на Yii с нуля. Урок 9. Выборка статей определённого автора. (13.08.2014) free
  7. 8 Разработка сайта на Yii с нуля. Урок 8. Вызов в моделях функции, общей для них. Как избегать дублирования кода? (25.07.2014) free
  8. 7 Разработка сайта на Yii с нуля. Урок 7. Изменение количества выводимых записей на странице в CGridView. Включаем сессии Yii. (04.09.2013) free
  9. 6 Разработка сайта на Yii с нуля. Урок 6. Автоматич. отправка оповещений об одобренных комментариях на email автора комментария (11.08.2013) free
  10. 5 Разработка сайта на Yii с нуля. Урок 5. Переименовываем blog в yiico. Изменяем "Home" в breadcrumbs. Включаем Gzip-сжатие. (09.08.2013) free
  11. 4 Курс по Yii с нуля. Урок 4. Дорабатываем простую работу с пользователями: хранение пароля при редактировании пользователя. (13.04.2013) free
  12. 3 Курс по Yii с нуля. Урок 3. Создаём новых пользователей. Организуем простую систему авторизации. Аутентификация, пароли, соли. (20.07.2014) ← вы тут free
  13. 2 Курс по Yii с нуля. Урок 2. Переносим и настраиваем Yii и проект нашего сайта на рабочем сервере. Избавляемся от index.php в url (09.07.2014) free
  14. 1 Курс по Yii с нуля. Урок 1. Устанавливаем Yii на локальном компьютере. Заводим проект будущего сайта. (10.03.2013) free

Комментарии (14)     Подпишитесь на RSS комментариев к этой статье.

14 комментариев

#285
cpentyc говорит:
February 21, 2012 at 10:17 am
Думаю нужно в правилах дать доступ не авторизованному пользователю к действию user/create))))
#286
cpentyc говорит:
February 21, 2012 at 10:52 am
У меня никто авторизоваться не может
#287
cpentyc говорит:
February 21, 2012 at 11:02 am

<?php echo $form->passwordField($model,'password',array('size'=>60,'maxlength'=>128,'value'=>'')); ?>

нужно сбрасывать пароли, то там хэш при неудачной валидации забит

Loco: Верно, но некритично.

#288
cpentyc говорит:
February 22, 2012 at 12:36 pm
  public function afterValidate()
  {
 
$this->password = self::hashPassword($this->password, $this->salt);
        if($this->isNewRecord) {

            $salt = self::generateSalt();

$this->password = self::hashPassword($this->password, $salt);

            $this->salt = $salt;

            //$this->role = 'user';

        }

        return true;

  }

#291
Саша Loco говорит:
February 22, 2012 at 05:22 pm

я делал регистрацию ручную, то есть только админы заводят новых пользователей. А так, да, разрешаете нужные экшены в контроллере для нужных пользователей.

Что в выделенной функции вы хотите сказать?

#305
cpentyc говорит:
February 23, 2012 at 09:52 am

При добавлении пользователя пароль генерится дважды .

public function afterValidate()
  {
 
        if($this->isNewRecord) {
            $salt = self::generateSalt();

$this->parent_id = (Yii::app()->user->parent_id==0?Yii::app()->user->id:Yii::app()->user->parent_id);
            $this->password = self::hashPassword($this->password, $salt);
            $this->salt = $salt;
            //$this->role = 'user';
return true;
        }
$this->password = self::hashPassword($this->password, $this->salt);
        return true;
  }

#308
Саша Loco говорит:
February 23, 2012 at 11:19 am
Может Yii::app()->end(); поставить в конце if (новая запись), чтобы дальше не генерировался пароль ещё раз?
#608
Сергей говорит:
August 14, 2012 at 03:55 pm
А что делать если я сделал изменение пароля и остальных данных в разных местах. Но когда меняю например имя то пароль все равно меняется((((((( что делать? afterValidate срабатывает в обоих случаях
#695
ElisDN говорит:
November 6, 2012 at 01:39 am
Удобнее всего добавить в модель пользователя дополнительную переменную для нового пароля. И если оно заполнено, то генерировать из него хэш. Я как раз писал об этом у себя.
#696
cpentyc говорит:
November 7, 2012 at 02:28 pm
Yii::app()->end(); делать нельзя. Проверенно горьким опытом
#698
almix говорит:
November 9, 2012 at 10:08 pm
Ответы на многие комментарии, возникшие выше смотрите в следующем уроке, с подсказки ElisDN
#705
zloyded говорит:
November 17, 2012 at 10:21 pm

Господа.
Можно сказать впервый взялся за YII.

Делаю регу пользователей. По вашей статье добавил ввод повторно пароля. теперь форма реги, пишет что поле (password_repeat) не может быть пустым. при чем весгда. Даже когда оно зааолнено. код тот же что и у Вас.

#708
almix говорит:
November 20, 2012 at 02:03 pm
zloyded, проверьте всё ли так в коде, скорее всего содержимое password_repeat не передаётся. Оно в правилах модели User в safe помещено? Читайте следующий урок, там это поле переназываем и улучшаем работу регистрации авторизации.
#728
sasha говорит:
December 19, 2012 at 11:33 pm
а как авторизироваться пользователю. если соль генерируется каждый раз новая, или надо в бд отдельное поле для неё вести?