В своем проекте (Вопросы и ответы для программистов) на Zend Framework мне стало необходимо подключить OpenID и после часа работы я успешно подключил стандартный зендовский сервис. Думаю класс, как легко и удобно(как и все в зенде), но как оказалось этот сервис не работает с OpenId 2.0, да — он просто не дописан.
Немного порывшись в исходниках я это подтвердил — Consumer.php * todo OpenID 2.0 (7.3) XRI and Yadis discovery
Потом посмотрел по багтрекеру и оказалось что это весит уже давно(очень) и никто не спешит доделывать. Тогда я и начал искать альтернативу. Выбор попал на openidenabled.com/php-openid.
Далее приведу пример, который позволит тем кто еще только собирается подключать сделать это минут за 15.
Качаем библиотеку с openidenabled.com, подключаем ее в php include path, или вручную, кому как удобно.
Что бы не делать то что уже сделано до меня, я взял за основу компонент из фреймфорка CakePhp.
Ниже код модифицированного под мои нужны компонента.
Как параметр передаем контроллер для вызова редиректа(openId предыдущей версии), но так как $this->_redirect(); это protected метод и его нельзя вызывать из других классов, я добавил обертку для него в классе контроллера(это лучше конечно делать через интерфейсы, но это дело лично каждого).
define('Auth_Yadis_CURL_OVERRIDE',true); — значит что будет использоваться file_get_contents а не curl(для discovery). У меня curl установлен но при работе с https, как у гугля, библиотека не выдает никаких ошибок, но и не работает. На это я еще потрачу свое время, но пока настроил что бы работало, скорее всего проблема именно в моих настройках или конфигурации сервера.
И так, как это использовать.
В LoginController делаем action, в который попадем после того как пользователь ввел свой ID в поле ввода и нажал логин.
Zend_Registry::getInstance()->configuration->webhost = это имя моего хоста, можно его получить из переменной SERVER(но мне он нужен и в других местах)
Zend_Registry::getInstance()->configuration->webhost.'/login/openidcallback/ — это куда OpendId сервер должен сделать редирект после логина(удачного или по нажатию отмена). Можно возвращаться обратно на /login/ с каким то параметром, и обрабатывать его, опять же дело личное.
Далее код который проверяет логин по возвращению с сервера OpenId:
Для хранения ассоциации между пользователем базы и OpenID идентификатором я использую таблицу. findByOpenid и addUserOpenidURL работают с ней. Я посчитал что «неизвестный» или «гость» неподходит для моего сайта, и если пользователь успешно прилогинился, но первый раз на сайте, я предлагаю выбрать имя пользователя, чтобы продолжить(этот пункт возможно окажется лишним для тех кто так не считает).
Для хранения OpenId ассоциация и nonces(не знаю как перевести получше) используется файловое хранилище, т.к для хранения в БД эта билиотека требует соединение к базе от PEAR, а мне не хотелось лишние объекты.
Надеюсь разработчики зенда все же решатся доделать реализацию OpenId и тогда прийдется писать перенос данных из формата этой библиотеки в формат зенда, думаю это не создаст особых проблем, и может послужит темой для отдельного топика.
Возможно кто то самостоятельно сделал патчи к zend framework и уже успешно использует родные зендовские сервисы, был бы очень благодарен если Вы ними поделитесь.
Если кому-то топик полезным, но недостаточно детальным, пишите комментарии, постараюсь дополнить.
P.S. прошу очень не придираться к Naming Conventions, я знаю я их не очень то соблюдаю :)
Немного порывшись в исходниках я это подтвердил — Consumer.php * todo OpenID 2.0 (7.3) XRI and Yadis discovery
Потом посмотрел по багтрекеру и оказалось что это весит уже давно(очень) и никто не спешит доделывать. Тогда я и начал искать альтернативу. Выбор попал на openidenabled.com/php-openid.
Далее приведу пример, который позволит тем кто еще только собирается подключать сделать это минут за 15.
Качаем библиотеку с openidenabled.com, подключаем ее в php include path, или вручную, кому как удобно.
Что бы не делать то что уже сделано до меня, я взял за основу компонент из фреймфорка CakePhp.
Ниже код модифицированного под мои нужны компонента.
<?php
class OpenidComponent {
private $controller = null;
public function __construct($controller) {
$this->controller = $controller;
define('Auth_Yadis_CURL_OVERRIDE',true);
}
public function authenticate($openidUrl, $returnTo, $realm, $required = array(), $optional = array()) {
if (trim($openidUrl) != '') {
if ($this->isEmail($openidUrl)) {
$openidUrl = $this->transformEmailToOpenID($openidUrl);
}
$consumer = $this->getConsumer();
$authRequest = $consumer->begin($openidUrl);
if (!isset($authRequest) || !$authRequest) {
throw new InvalidArgumentException('Invalid OpenID');
}
if ($authRequest->shouldSendRedirect()) {
$redirectUrl = $authRequest->redirectUrl($realm, $returnTo);
if (Auth_OpenID::isFailure($redirectUrl)) {
throw new Exception('Could not redirect to server: '.$redirectUrl->message);
} else {
$this->controller->redirect($redirectUrl);
}
} else {
$formId = 'openid_message';
$formHtml = $authRequest->formMarkup($realm, $returnTo, false , array('id' => $formId));
if (Auth_OpenID::isFailure($formHtml)) {
throw new Exception('Could not redirect to server: '.$formHtml->message);
} else {
return '<html><head><title>редирект на страницу OpenId сервера</title></head>'.
"<body onload='document.getElementById(\"".$formId."\").submit()'>".
$formHtml.'</body></html>';
}
}
}
}
public function getResponse($currentUrl) {
$consumer = $this->getConsumer();
$response = $consumer->complete($currentUrl, $this->getQuery());
return $response;
}
private function getConsumer() {
require_once 'Auth/OpenID/Consumer.php';
return new Auth_OpenID_Consumer($this->getFileStore());
}
private function getQuery() {
$query = Auth_OpenID::getQuery();
// unset the url parameter automatically added by app/webroot/.htaccess
// as it causes problems with the verification of the return_to url
unset($query['url']);
return $query;
}
private function isEmail($string) {
return strpos($string, '@');
}
private function transformEmailToOpenID($email) {
if (include_once 'My/Auth/Yadis/Email.php') {
return Auth_Yadis_Email_getID($email);
}
throw new InvalidArgumentException('Invalid OpenID');
}
private function getFileStore() {
require_once 'Auth/OpenID/FileStore.php';
$storePath = Zend_Registry::getInstance()->configuration->openidFileStore;
if (!file_exists($storePath) && !mkdir($storePath,0777)) {
throw new Exception('Could not create the FileStore directory '.$storePath.'. Please check the effective permissions.');
}
return new Auth_OpenID_FileStore($storePath);
}
}
* This source code was highlighted with Source Code Highlighter.
public function openid(){
if (null === $this->_openid) {
require_once APPLICATION_PATH . '/models/openid.php';
$this->_openid = new OpenidComponent($this);
}
return $this->_openid;
}
* This source code was highlighted with Source Code Highlighter.
Как параметр передаем контроллер для вызова редиректа(openId предыдущей версии), но так как $this->_redirect(); это protected метод и его нельзя вызывать из других классов, я добавил обертку для него в классе контроллера(это лучше конечно делать через интерфейсы, но это дело лично каждого).
public function redirect($url){
$this->_redirect($url);
}
* This source code was highlighted with Source Code Highlighter.
define('Auth_Yadis_CURL_OVERRIDE',true); — значит что будет использоваться file_get_contents а не curl(для discovery). У меня curl установлен но при работе с https, как у гугля, библиотека не выдает никаких ошибок, но и не работает. На это я еще потрачу свое время, но пока настроил что бы работало, скорее всего проблема именно в моих настройках или конфигурации сервера.
И так, как это использовать.
В LoginController делаем action, в который попадем после того как пользователь ввел свой ID в поле ввода и нажал логин.
public function openidAction(){
error_reporting(E_ERROR);
$auth = Zend_Auth::getInstance();
$flashMessenger = $this->_helper->FlashMessenger;
$this->_helper->layout->disableLayout();
$this->_helper->viewRenderer->setNoRender();
$identifier = trim($this->getRequest()->getParam("openid_identifier"));
$openidComponent = $this->openid();
try{
$ret = $openidComponent->authenticate($identifier,Zend_Registry::getInstance()->configuration->webhost.'/login/openidcallback/',Zend_Registry::getInstance()->configuration->webhost, $required = array(), $optional = array());
if ($ret){
echo $ret;
}
}catch(Exception $e){
Zend_Registry::getInstance()->logger->ERR("openid error:".$e->getMessage().$e->getTraceAsString());
$flashMessenger->addMessage("Неправильный openID!");
return $this->_redirect('/login/');
}
}
* This source code was highlighted with Source Code Highlighter.
Zend_Registry::getInstance()->configuration->webhost = это имя моего хоста, можно его получить из переменной SERVER(но мне он нужен и в других местах)
Zend_Registry::getInstance()->configuration->webhost.'/login/openidcallback/ — это куда OpendId сервер должен сделать редирект после логина(удачного или по нажатию отмена). Можно возвращаться обратно на /login/ с каким то параметром, и обрабатывать его, опять же дело личное.
Далее код который проверяет логин по возвращению с сервера OpenId:
public function openidcallbackAction(){
$openidComponent = $this->openid();
$response = $openidComponent->getResponse(Zend_Registry::getInstance()->configuration->webhost.'/login/openidcallback/');
$flashMessenger = $this->_helper->FlashMessenger;
if ($response->status == Auth_OpenID_CANCEL) {
$flashMessenger->addMessage('Верификация была отменена!');
return $this->_redirect('/login/');
} else if ($response->status == Auth_OpenID_FAILURE) {
$flashMessenger->addMessage("Ошибка авторизации: $response->message !");
return $this->_redirect('/login/');
} else if ($response->status == Auth_OpenID_SUCCESS) {
$auth = Zend_Auth::getInstance();
$openid = $response->getDisplayIdentifier();
$model = $this->getUserModel();
//look for user, if not found suggest to choose userName on site
$user = $model->findByOpenid($openid);
if ($user){
$auth->getStorage()->write($user);
return $this->_redirect("/");//на главную
}
$flashMessenger->addMessage('Вы первый раз на сайте. Пожалуйста, выберите имя пользователя!');
$model = Lookup::get()->user();
$newUser = $model->create();
$newUser->save();
$auth->getStorage()->write($newUser);
$openid = $response->getDisplayIdentifier();
$model->addUserOpenidURL($newUser, $openid);
return $this->_redirect('/settings/choosename/');
}
}
* This source code was highlighted with Source Code Highlighter.
Для хранения ассоциации между пользователем базы и OpenID идентификатором я использую таблицу. findByOpenid и addUserOpenidURL работают с ней. Я посчитал что «неизвестный» или «гость» неподходит для моего сайта, и если пользователь успешно прилогинился, но первый раз на сайте, я предлагаю выбрать имя пользователя, чтобы продолжить(этот пункт возможно окажется лишним для тех кто так не считает).
Для хранения OpenId ассоциация и nonces(не знаю как перевести получше) используется файловое хранилище, т.к для хранения в БД эта билиотека требует соединение к базе от PEAR, а мне не хотелось лишние объекты.
Надеюсь разработчики зенда все же решатся доделать реализацию OpenId и тогда прийдется писать перенос данных из формата этой библиотеки в формат зенда, думаю это не создаст особых проблем, и может послужит темой для отдельного топика.
Возможно кто то самостоятельно сделал патчи к zend framework и уже успешно использует родные зендовские сервисы, был бы очень благодарен если Вы ними поделитесь.
Если кому-то топик полезным, но недостаточно детальным, пишите комментарии, постараюсь дополнить.
P.S. прошу очень не придираться к Naming Conventions, я знаю я их не очень то соблюдаю :)