Pull to refresh

Единая авторизация пользователей с поддержкой нескольких сервисов

Reading time6 min
Views67K
Yii EAuth extension При реализации одного из проектов на Yii framework у меня возникла задача сделать регистрацию и авторизацию пользователей через сторонние сервисы (Google, Facebook, Twitter, etc).

У данной задачи есть два пути решения:
  • Использовать сервис авторизации, например Loginza;
  • Реализовывать функции авторизации самостоятельно для каждого сервиса.
UPDATE: Актуальная версия и инструкция по настройке доступны на github.com. Инструкция в данной статье подходит для EAuth версии <= 1.1.3.

Плюсы использования сервиса авторизации:
  1. Простота и скорость установки;
  2. Нет необходимости изучать тонкости авторизации через каждого провайдера.

Однако самостоятельная авторизация имеет ряд других преимуществ:
  1. Полный контроль над процессом авторизации: что будет написано в окне авторизации провайдера, какие данные мы получим и т.д.;
  2. Возможность изменять внешний вид виджета авторизации в соответствии с дизайном сайта;
  3. При авторизации через OAuth есть возможность вызывать методы API, если их предоставляет провайдер;
  4. Меньше зависимостей от сторонних сервисов – больше надежность.

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

1. Требования к системе авторизации


При проектировании расширения я руководствовался следующими принципами:
  1. Необходимость абстрагироваться от тонкостей авторизации через различные типы сервисов, использование адаптеров для каждого сервиса.
  2. Получение уникального идентификатора авторизации, который можно использовать для регистрации пользователя в нашем приложении.
  3. Возможность расширения стандартных классов авторизации для получения дополнительных данных о пользователе.
  4. Возможность работать с API социальных сетей путем расширения класса авторизации необходимого сервиса.
  5. Возможность настраивать список поддерживаемых сайтом сервисов, переопределять внешний вид виджета авторизации. Возможность использовать popup окно для авторизации без закрытия нашего приложения.

2. Расширение EAuth


В результате реализации всех требований выше на свет появилось расширение EAuth.
На данный момент расширение содержит:
  • Компонент, содержащий вспомогательные функции.
  • Виджет, выводящий список сервисов в виде иконок и позволяющий проводить авторизацию в popup окне.
  • Базовые классы для добавления сервисов, основанных на OpenID или OAuth.
  • Готовые классы для авторизации через Google, Яндекс, Twitter, Facebook, ВКонтакте и Mail.ru.

3. Установка


Для начала необходимо скачать и распаковать расширение в `protected/extensions/eauth`.

3.1 Зависимости


Расширение использует loid и EOAuth для работы с OpenID и OAuth соответственно. Данные расширения необходимо скачать и поместить в каталог `protected/extensions`.

3.2 Настройка


В конфигурацию `main.php` необходимо добавить:
'import'=>array(
	'ext.eoauth.*',
	'ext.eoauth.lib.*',
	'ext.lightopenid.*',
	'ext.eauth.services.*',
),

'components'=>array(
	'loid' => array(
	    'class' => 'ext.lightopenid.loid',
	),
	'eauth' => array(
	    'class' => 'ext.eauth.EAuth',
	    'popup' => true, // Use the popup window instead of redirecting.
	    'services' => array( // You can change the providers and their classes.
	        'google' => array(
	            'class' => 'GoogleOpenIDService',
	        ),
	        'yandex' => array(
	            'class' => 'YandexOpenIDService',
	        ),
	        'twitter' => array(
	            'class' => 'TwitterOAuthService',
	            'key' => '...',
	            'secret' => '...',
	        ),
	        'facebook' => array(
	            'class' => 'FacebookOAuthService',
	            'client_id' => '...',
	            'client_secret' => '...',
	        ),
	        'vkontakte' => array(
	            'class' => 'VKontakteOAuthService',
	            'client_id' => '...',
	            'client_secret' => '...',
	        ),
	        'mailru' => array(
	            'class' => 'MailruOAuthService',
	            'client_id' => '...',
	            'client_secret' => '...',
	        ),
	    ),
	),
),

Расширение имеет всего два параметра: popup и services. Параметр popup отвечает за использование для авторизации popup окна, вместо перенаправления на сайт провайдера. Параметр services представляет собой список провайдеров, поддерживаемых нашим приложением. Для каждого провайдера можно указать собственный класс, основанный на базовом классе провайдера. Чтобы получить ключи для OAuth провайдеров необходимо зарегистрировать ваше приложение у соответствующего провайдера.

4. Использование


В качестве примера возьмем стандартное приложение Yii, сгенерированное командой `yiic webapp create`, и добавим возможность авторизации через Google и Яндекс (OAuth провайдеров подключать не будем, чтобы не возиться с ключами). Посмотреть готовую демку.

4.1 UserIdentity


Для начала создадим класс ServiceUserIdentity, отвечающий за вход с помощью нашего расширения. Код класса:
<?php
class ServiceUserIdentity extends UserIdentity {
	const ERROR_NOT_AUTHENTICATED = 3;

	/**
	 * @var EAuthServiceBase the authorization service instance.
	 */
	protected $service;
	
	/**
	 * Constructor.
	 * @param EAuthServiceBase $service the authorization service instance.
	 */
	public function __construct($service) {
	    $this->service = $service;
	}
	
	/**
	 * Authenticates a user based on {@link username}.
	 * This method is required by {@link IUserIdentity}.
	 * @return boolean whether authentication succeeds.
	 */
	public function authenticate() {        
	    if ($this->service->isAuthenticated) {
	        $this->username = $this->service->getAttribute('name');
	        $this->setState('id', $this->service->id);
	        $this->setState('name', $this->username);
	        $this->setState('service', $this->service->serviceName);
	        $this->errorCode = self::ERROR_NONE;        
	    }
	    else {
	        $this->errorCode = self::ERROR_NOT_AUTHENTICATED;
	    }
	    return !$this->errorCode;
	}
}

Стандартные классы провайдеров предоставляют нам два атрибута: id и name. Кроме того, каждый провайдер имеет свой идентификатор, который содержится в свойстве serviceName. В классе ServiceUserIdentity мы сохраняем данные атрибуты в сессию (и в cookie) текущего пользователя.

4.2 Редактирование SiteContoller


Вторым шагом будет изменение действия `site/login`. Добавим следующий код в начало действия:
public function actionLogin() {
	$service = Yii::app()->request->getQuery('service');
	if (isset($service)) {
	    $authIdentity = Yii::app()->eauth->getIdentity($service);
	    $authIdentity->redirectUrl = Yii::app()->user->returnUrl;
	    $authIdentity->cancelUrl = $this->createAbsoluteUrl('site/login');
	    
	    if ($authIdentity->authenticate()) {
	        $identity = new ServiceUserIdentity($authIdentity);
	        
	        // Успешный вход
	        if ($identity->authenticate()) {
	            Yii::app()->user->login($identity);
	            
	            // Специальный редирект с закрытием popup окна
	            $authIdentity->redirect();
	        }
	        else {
	            // Закрываем popup окно и перенаправляем на cancelUrl
	            $authIdentity->cancel();
	        }
	    }
	    
	    // Что-то пошло не так, перенаправляем на страницу входа
	    $this->redirect(array('site/login'));
	}
	
	// далее стандартный код...
}

Сначала мы проверяем наличие переменной $_GET[‘service’]. Если такая переменная есть, то создаем экземпляр класса провайдера и настраиваем пути для перенаправления и отмены авторизации. Затем вызываем метод `$authIdentity->authenticate()`, который проделывает для нас всю магию. Методы `$authIdentity->redirect();` и `$authIdentity->cancel();` необходимы для корректного закрытия popup окна, если оно используется.

4.3 Редактируем представление `protected/views/site/login.php`


Для использования стандартного виджета достаточно добавить пару строк после основной формы:
<h2>Do you already have an account on one of these sites? Click the logo to log in with it here:</h2>
<?php Yii::app()->eauth->renderWidget(); ?>

Для изменения внешнего вида виджета можно скопировать файл `protected/extensions/eauth/views/auth.php` в `[theme_name]/views/EAuthWidget/auth.php`.

4.4 Результат


После всех проделанных действий мы можем открыть наш сайт и перейти на страницу Login. После стандартной формы авторизации появятся иконки сервисов авторизации:
Иконки авторизации

При клике, например, по иконке Google откроется popup окно: Popup окно авторизации

Если пользователь подтвердит авторизацию для нашего приложения, то пользователь будет авторизован и произойдет перенаправление на страницу redirectUrl (в примере это Yii::app()->user->returnUrl). Если же пользователь нажмет No, thanks, то popup окно будет просто закрыто.

Заключение


Вот и все, система авторизации готова к работе. Что еще можно делать с расширением:
  • Получать дополнительную информацию о пользователе путем расширения классов провайдеров. Пример можно увидеть в демо при авторизации через Вконтакте;
  • Интегрировать расширение с yii-user. В общем случае для регистрации пользователя достаточно добавить в таблицу {{users}} всего два поля: service и identity. Первое поле – название сервиса авторизации (свойство serviceName). Второе – уникальный идентификатор пользователя на этом сервисе (свойство id);
  • Использовать расширение для работы с API социальных сетей.

Ссылки



UPDATE: в пункте «3.2 Настройка» не хватало расширения loid, добавил.
UPDATE 2: Актуальная версия и инструкция по настройке доступны на github.com. Инструкция в данной статье подходит для EAuth версии <= 1.1.3.
Tags:
Hubs:
+124
Comments105

Articles