Не так давно на Хабре была статья про виджет авторизации 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
