Не так давно на Хабре была статья про виджет авторизации uLogin.
Что мне в нём очень понравилось, это возможность указать обязательные поля, при этом, в случае их неполучения от провайдера, пользователю предлагается врчуную их заполнить. Так возникло желания написать модуль в Kohana, который позволял бы легко осуществлять регистрацию пользователя с помощью виджета uLogin.
Каждый модуль помещается в отдельную папку в папке MODPATH (по умолчанию modules)
Внутри могут находится папки classes, config, и все те же самые, что и в application. Это так называемая каскадная файловая система, подробнее здесь
Для нашего модуля понадобятся только папки classes и config.
В папке config будет лежать конфигурационный файл по умолчанию, а, при желании, его можно переопределить в application/config.
В папке classes помещается файл ulogin.php, содержащий определение класса Ulogin:
Этот класс наследуется от Kohana_Ulogin, что позволит, при необходимости, переопределить его, поместив в файл application/classes/ulogin.php.
Так базовый класс называется Kohana_Ulogin, то должен находится в classes/kohana/ulogin.php, так как Kohana заменяет подчёркивание на слеш при поиске класса для автозагрузки.
Перед использованием модуль необходимо подключить в bootstrap.php:
Отображение виджета авторизации осуществляется следующим образом:
Что при этом происходит:
Статическая функция
Создаёт экземпляр класса Ulogin, при этом передавая эму массив с конфигурацией, это сделано для возможности перезаписывать настройки по умолчанию непосредственно при вызове.
Конструктор класса:
Загружает конфиг с помощью функции Коханы Kohana::$config->load и проверяет redirect_uri на NULL. В случае положительного результат проверки просто устанавливается текущий адрес, по которому обратился пользователь, получая его с помощью вызова Request::initial()->url(true). Параметр true говорит Кохане о необходимости включить протокол в адрес. Request::initial() используется для того, что бы получить именно начальный адрес.
Функция render():
Первым делом надо составить параметры для передачи скрипту uLogin. http_build_query() тут не подходит, так как он кодирует данные (в частностия запятые) в их шестнадцатеричные коды (%xx). Потом проверяется, первый это запуск (в массиве self::$_used_id пусто), или нет.
Если первый, то загружается View ulogin/first.php:
В нём, в зависимости от выбранного режима, отображается HTML-код вызова виджета.
Если это втророй запсук, то генерируем уникальный идентификатор и загруажаем View ulogin/second.php:
В нём просто вызывается виджет согласно документации, расположенной на официальном сайте
Вот и всё, что необходимо для отображения виджета авторизации
Обработка с помощью модуля примерно выглядит так:
То есть проверяем режим с помощью вызова mode(), после чего либо отображаем виджет, либо авторизуем/регистрируем пользователя. try/catch нужен для проверки правильности регистрации пользователя, например совпадающий e-mail/
Функция mode() просто проверят наличие $_POST['token']:
C login() посложнее:
Что здесь происходит:
Повторно проверяем токен (вдруг кто-то вызвал просто login()), после чего пытаемся получить хост для передачи uLogin'у: либо из базового адреса (указывается в bootstrap.php в Kohana::init()), либо через $_SERVER['HTTP_HOST'] или $_SERVER['SERVER_NAME'].
После чего делаем запрос на uLogin, для получения данных пользователя:
В результате получаем нечто такое (пример для Google):
Далее пытаемся найти его в базе данных, используй его уникальный урл: $user['identity']:
Если найден ($orm_user->loaded() == true), просто авторизуем его:
В обратном случае его надо зарегистрировать:
Исходные коды доступны на github
Как видите, и использование uLogin, и написание модулей к Kohana, не представляют никаких сложностей.
Удачи!
UPD: Добавил View'ы
UPD2: Переписал немного код и статью, спасибо dohlik
Что мне в нём очень понравилось, это возможность указать обязательные поля, при этом, в случае их неполучения от провайдера, пользователю предлагается врчуную их заполнить. Так возникло желания написать модуль в 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);
В обратном случае его надо зарегистрировать:
- Составляем имя пользователя:
$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']);
- Заполняем поля модели:
$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);
- Создаём пользователя, добавляем ему роль 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