Как стать автором
Поиск
Написать публикацию
Обновить

Пишем модуль uLogin для Kohana 3.2

Время на прочтение6 мин
Количество просмотров8.8K
Не так давно на Хабре была статья про виджет авторизации uLogin.
Что мне в нём очень понравилось, это возможность указать обязательные поля, при этом, в случае их неполучения от провайдера, пользователю предлагается врчуную их заполнить. Так возникло желания написать модуль в Kohana, который позволял бы легко осуществлять регистрацию пользователя с помощью виджета uLogin.


Немного о структуре модулей


Каждый модуль помещается в отдельную папку в папке MODPATH (по умолчанию modules)
Внутри могут находится папки classes, config, и все те же самые, что и в application. Это так называемая каскадная файловая система, подробнее здесь

Для нашего модуля понадобятся только папки classes и config.
В папке config будет лежать конфигурационный файл по умолчанию, а, при желании, его можно переопределить в application/config.
В папке classes помещается файл ulogin.php, содержащий определение класса Ulogin:
<?php defined('SYSPATH') or die('No direct script access.');

class Ulogin extends Kohana_Ulogin {}

Этот класс наследуется от Kohana_Ulogin, что позволит, при необходимости, переопределить его, поместив в файл application/classes/ulogin.php.

Так базовый класс называется Kohana_Ulogin, то должен находится в classes/kohana/ulogin.php, так как Kohana заменяет подчёркивание на слеш при поиске класса для автозагрузки.

Перед использованием модуль необходимо подключить в bootstrap.php:
Kohana::modules(array(
	......
	'ulogin'  => MODPATH.'ulogin',  // uLogin

	));


Отображение виджета

Отображение виджета авторизации осуществляется следующим образом:
echo Ulogin::factory()->render()


Что при этом происходит:

Статическая функция
	public static function factory(array $config = array())
	{
		return new Ulogin($config);
	}

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

Конструктор класса:
		public function __construct(array $config = array())
	{
		$this->config = array_merge($this->config, Kohana::$config->load('ulogin')->as_array(), $config);
		
		if ($this->config['redirect_uri'] === NULL)
			$this->config['redirect_uri'] = Request::initial()->url(true);
	}

Загружает конфиг с помощью функции Коханы Kohana::$config->load и проверяет redirect_uri на NULL. В случае положительного результат проверки просто устанавливается текущий адрес, по которому обратился пользователь, получая его с помощью вызова Request::initial()->url(true). Параметр true говорит Кохане о необходимости включить протокол в адрес. Request::initial() используется для того, что бы получить именно начальный адрес.

Функция render():
	public function render()
	{	
		$params = 	
			'display='.$this->config['type'].
			'&fields='.implode(',', array_merge($this->config['username'], $this->config['fields'])).
			'&providers='.implode(',', $this->config['providers']).
			'&hidden='.implode(',', $this->config['hidden']).
			'&redirect_uri='.$this->config['redirect_uri'].
			'&optional='.implode(',', $this->config['optional']);
			
		if (count(self::$_used_id) == 0)
		{
			$view = View::factory('ulogin/first');
			
			self::$_used_id[] = 'uLogin';
		}
		else
		{
			$view = View::factory('ulogin/second');
			
			do
			{
				$uniq_id = "uLogin_".rand();
			}
			while(in_array($uniq_id, self::$_used_id));
			
			self::$_used_id[] = $uniq_id;
			
			$view->set('uniq_id', $uniq_id);
		}
		
		return $view->set('cfg', $this->config)->set('params', $params)->render();
	}

Первым делом надо составить параметры для передачи скрипту uLogin. http_build_query() тут не подходит, так как он кодирует данные (в частностия запятые) в их шестнадцатеричные коды (%xx). Потом проверяется, первый это запуск (в массиве self::$_used_id пусто), или нет.
Если первый, то загружается View ulogin/first.php:
<?php if ($cfg['type'] == 'window') :?>
	
	<a href="#" id="uLogin">
		<img src="http://ulogin.ru/img/button.png" width=187 height=30 alt="МультиВход"/>
	</a>
	<script src="http://ulogin.ru/js/widget.js?<?php echo $params; ?>"></script>
	
<?php else: ?>
	
	<div id="uLogin"></div>
	<script src="http://ulogin.ru/js/widget.js?<?php echo $params; ?>"></script>

<?php endif; ?>

В нём, в зависимости от выбранного режима, отображается HTML-код вызова виджета.

Если это втророй запсук, то генерируем уникальный идентификатор и загруажаем View ulogin/second.php:
<div id="<?php echo $uniq_id; ?>"></div>
<script type='text/javascript'>uLogin.init('id=<?php echo $uniq_id; ?>&<?php echo $params; ?>');</script>

В нём просто вызывается виджет согласно документации, расположенной на официальном сайте

Вот и всё, что необходимо для отображения виджета авторизации

Обработка результата


Обработка с помощью модуля примерно выглядит так:
		$ulogin = Ulogin::factory();
		if (!$ulogin->mode())
			$this->template->content = $ulogin->render();
		else
		{
			try
			{
				$ulogin->login();
			}
			catch(ORM_Validation_Exception $e)
			{
				$this->template->errors = $e->errors('');
			}
		}


То есть проверяем режим с помощью вызова mode(), после чего либо отображаем виджет, либо авторизуем/регистрируем пользователя. try/catch нужен для проверки правильности регистрации пользователя, например совпадающий e-mail/

Функция mode() просто проверят наличие $_POST['token']:
	public function mode()
	{
		return !empty($_POST['token']);
	}


C login() посложнее:
	public function login()
	{
		if (empty($_POST['token']))
			throw new Kohana_Exception('Empty token.');
			
		if (!($domain = parse_url(URL::base(), PHP_URL_HOST)))
		{
			$domain = isset($_SERVER['HTTP_HOST']) ? $_SERVER['HTTP_HOST'] : $_SERVER['SERVER_NAME'];
		}
		
		$s = Request::factory('http://ulogin.ru/token.php?token=' . $_POST['token'] . '&host=' . $domain)->execute()->body();
		$user = json_decode($s, true);
		
		$orm_user = ORM::factory('user', array('identity' => $user['identity']));
		
		if (!$orm_user->loaded())
		{
			$data['username'] = '';
			foreach($this->config['username'] as $part_of_name)
				$data['username'] .= (empty($user[$part_of_name]) ? '' : (' '.$user[$part_of_name]));
			
			$data['username'] = trim($data['username']);
			
			if (!$data['username'])
				throw new Kohana_Exception('Username fields not set in config/ulogin.php');
				
			$data['password'] = 'ulogin_autogenerated_password';
			
			$data['identity'] = $user['identity'];
			$data['network'] = $user['network'];
			
			$cfg_fields = array_merge($this->config['fields'], $this->config['optional']);
			foreach($cfg_fields as $field)
			{
				if (!empty($user[$field]))
					$data[$field] = $user[$field];
			}
			
			$orm_user->values($data);
			$orm_user->create();
			$orm_user->add('roles', ORM::factory('role', array('name' => 'login')));
			
			Auth::instance()->force_login($orm_user);
		}
		else
		{
			Auth::instance()->force_login($orm_user);
		}
	}


Что здесь происходит:
Повторно проверяем токен (вдруг кто-то вызвал просто login()), после чего пытаемся получить хост для передачи uLogin'у: либо из базового адреса (указывается в bootstrap.php в Kohana::init()), либо через $_SERVER['HTTP_HOST'] или $_SERVER['SERVER_NAME'].

После чего делаем запрос на uLogin, для получения данных пользователя:

		$s = Request::factory('http://ulogin.ru/token.php?token=' . $_POST['token'] . '&host=' . $domain)->execute()->body();
		$user = json_decode($s, true);

В результате получаем нечто такое (пример для Google):
array(6) (
 	"network" => string(6) "google"
	"identity" => string(50) "Уникальный УРЛ пользователя, например https://plus.google.com/u/0/google+ идентификатор/"
	"uid" => string(21) "google+ идентификатор"
	"email" => string(21) "e-mail"
	"first_name" => string(10) "Имя"
	"last_name" => string(14) "Фамилия"
)


Далее пытаемся найти его в базе данных, используй его уникальный урл: $user['identity']:
$orm_user = ORM::factory('user', array('identity' => $user['identity']));

Если найден ($orm_user->loaded() == true), просто авторизуем его:
Auth::instance()->force_login($orm_user);

В обратном случае его надо зарегистрировать:

  1. Составляем имя пользователя:
    $data['username'] = '';
    foreach($this->config['username'] as $part_of_name)
    	$data['username'] .= (empty($user[$part_of_name]) ? '' : (' '.$user[$part_of_name]));
    			
    $data['username'] = trim($data['username']);


  2. Заполняем поля модели:
    $data['password'] = 'ulogin_autogenerated_password';
    			
    $data['identity'] = $user['identity'];
    $data['network'] = $user['network'];
    $cfg_fields = array_merge($this->config['fields'], $this->config['optional']);
    foreach($cfg_fields as $field)
    {
    	if (!empty($user[$field]))
    		$data[$field] = $user[$field];
    }
    
    $orm_user->values($data);
    

  3. Создаём пользователя, добавляем ему роль login, после чего авторизуем его:
    $orm_user->create();
    $orm_user->add('roles', ORM::factory('role', array('name' => 'login')));
    Auth::instance()->force_login($orm_user);



Исходные коды доступны на github

Как видите, и использование uLogin, и написание модулей к Kohana, не представляют никаких сложностей.
Удачи!

UPD: Добавил View'ы
UPD2: Переписал немного код и статью, спасибо dohlik
Теги:
Хабы:
Всего голосов 22: ↑20 и ↓2+18
Комментарии20

Публикации

Ближайшие события