OpenID реализация

Для реализации вам потребуется:
  1. добавить поле user_openid в таблицу пользователей

  2. Изменить форму входа, которая на каджой странице

  3. Изменить форму входа, которая на странице входа

  4. Изменить модельку пользователя

  5. Изменить actionRegister


Кстати кат я переделал, у меня нормально работал. В tinyMCE — обрезает по [cut]

С первым пунктом все понятно

Находим файл
/templates/skin/habra/header.tpl

Я переделал форму целиком, т.к. сверстано было кривовато. Вот что получилось:

<div id="login-and-pass">
	<label>Логин или e-mail: <input type="text" name="login" value="" size="15"/></label>
	<label>Пароль: <input type="password" name="password" value="" size="15"/></label>
</div>

<div id="openid-field">
	<label>OpenID: <input type="text" name="openid" value="" size="15"></label>
</div>
<div>
	<a id="openid-switch">OpenID</a>
	<input type="submit" name="submit_login" value="{$aLang.user_login_submit}" size="15">
</div>

Дописал простенький скрипт для переключения:

window.addEvent('domready', function() {
	if ($('openid-switch')) {
		$('openid-switch').shown = false;
		$('openid-field').setStyle('display', 'none');
		$('openid-switch').addEvent('click', function(e) {
			if ($('openid-switch').shown) {
				$('login-and-pass').setStyle('display', 'block');
				$('openid-field').setStyle('display', 'none');
				$('openid-switch').shown = false;
			} else {
				$('login-and-pass').setStyle('display', 'none');
				$('openid-field').setStyle('display', 'block');
				$('openid-field').getElement('input').focus();
				$('openid-switch').shown = true;
			}
		});
	}
});


Затем открываем
templates/skin/habra/actions/ActionLogin/index.tpl
заменяем форму логина на это

<p id="login-and-pass">
	<span class="form">Логин или e-mail:</span><br />
	<input type="text" name="login" id="name" value="" size="25" /><br />
	
	<label for="password"><span class="form">Пароль:</span></label><br />
	<input type="password" id="password" name="password" value="" size="25" />
</p>

<p id="openid-field">
	<label>
		<span class="form">OpenID: </span>
		<input type="text" name="openid" value="" size="25">
	</label>
</div>
<p>

<p> <a id="openid-switch">OpenID</a> <input type="submit" name="submit_login" value="войти" /></p>

Не забываем подклюючить в header.light.tpl js скрипты для переключения

<script type="text/javascript" src="{$DIR_WEB_ROOT}/classes/lib/external/MooTools_1.2/mootools-1.2.1-core-yc.js"></script>
<script type="text/javascript" src="{$DIR_STATIC_SKIN}/js/main.js?v=11"></script>


Создаем страницу регистрации с OpenID. Новый файл в папку
templates/skin/habra/actions/ActionRegistration/openid.tpl

С примерно таким содержанием:

{include file='header.light.tpl'}

{include file='system_message.tpl'}


<table width="100%" border="0" cellspacing="4" cellpadding="4">
	<tr>
		<td align="left">
			<p>
				<span class="header">Попробуем зарегистрироваться?</span>
				<p>
					<span class="txt">Пожалуйста, заполните поля ниже. Это нужно сделать обязательно, иначе ничего не получится.</span>
				</p>
				<h4>{$_aRequest.openid_orig}</h4>
				<form action="{$DIR_WEB_ROOT}/registration/openid" method="POST" id="RegisterForm">
					<label for="login"><span class="form">Имя пользователя:</span></label><br />
					<input type="text" name="login" tabindex="1" id="login" value="{$_aRequest.login}" size="20" />
					
					<span class="form_note">Может состоять только из букв (A-Z a-z), цифр (0-9). Знак подчеркивания (_) лучше не использовать. Длина имени не может быть меньше 3 и больше 20 символов.</span><br />	
	
					<p>
						<label for="email"><span class="form">Электропочта:</span></label><br />
						<input type="text" id="email" style="width: 25em;" name="mail" value="{$_aRequest.mail}" size="25" tabindex="3" />
						<span class="form_note">
Для проверки регистрации и в целях безопасности нам нужен адрес вашей электропочты.</span><br />
					</p>
		
		
	 				<p>
	 					<label for="captcha"><span class="form">Нам нужны эти цифры:</span></label>
	 					<img src="{$DIR_WEB_ROOT}/classes/lib/external/captcha.php">

	 					<input type="text" style="text-align: center;" name="captcha" value="" maxlength=6 size=9 tabindex="6">

	 				</p>
 	
 			</p>

			<p class="l-bot">
				<input type="submit" name="submit_openid" tabindex="7" value="зарегистрироваться" />
			</p>
			</form>

			<p>
				<span class="txt_small">Может быть, перейти на <a href="{$DIR_WEB_ROOT}/">заглавную страницу</a>?</span><br />
			</p>
		</td>
	</tr>
</table>


{include file='footer.light.tpl'}

Идем править пых-код


В
classes/lib/external
Кладем class.openid.php
В
classes/actions/ActionRegistration.class.php
добавляем новый метод


	function EventOpenid() {
		//var_dump($_REQUEST);
		require DIR_SERVER_ROOT . '/classes/lib/external/class.openid.php';
		if (isset($_SESSION['openid_orig'])) {
			$_REQUEST['openid_orig'] = $_SESSION['openid_orig'];
		}
		if (isset($_REQUEST['openid_mode'])) {
			$openid = new SimpleOpenID;
			$openid->SetIdentity($_REQUEST['openid_identity']);
			$openid_validation_result = $openid->ValidateWithServer();
			if ($openid_validation_result == true) { 		// OK HERE KEY IS VALID
				// проверяем автопредложение ника (выдергиваем из openid_identity и проверяем на занятость
				if (isset($_REQUEST['openid_op_endpoint']) && $_REQUEST['openid_op_endpoint'] == 'http://openid.yandex.ru/server/') {
					$login = str_replace('http://openid.yandex.ru/', '', $_REQUEST['openid_identity']);
					$login = str_replace('/', '', $login);
					if (!$this->User_GetUserByLogin($login)) {
						$_REQUEST['login'] = $login;
					}
				} else if (isset($_REQUEST['openid_op_endpoint']) && $_REQUEST['openid_op_endpoint'] == 'http://www.myopenid.com/server') {
					$login = str_replace('http://', '', $_REQUEST['openid_identity']);
					$login = str_replace('.myopenid.com', '', $login);
					if (!$this->User_GetUserByLogin($login)) {
						$_REQUEST['login'] = $login;
					}
				} else {
					$login = str_replace('http://', '', $_REQUEST['openid_identity']);
					if (!$this->User_GetUserByLogin($login)) {
						$_REQUEST['login'] = $login;
					}
				}
				
				// проверяем автопредложение мыла
				if ($_REQUEST['openid_sreg_email'] && 
					!$this->User_GetUserByMail($_REQUEST['openid_sreg_email'])) {
					
					$_REQUEST['mail'] = $_REQUEST['openid_sreg_email'];
				}
				$_SESSION['openidInfo']['ident'] = $_REQUEST['openid_identity'];
				
			} else if($openid->IsError() == true) {			// ON THE WAY, WE GOT SOME ERROR
				$error = $openid->GetError();
				echo "ERROR CODE: " . $error['code'] . "
";
				echo "ERROR DESCRIPTION: " . $error['description'] . "
";
			} else {											// Signature Verification Failed
				echo "INVALID AUTHORIZATION";
			}
		} else if (isset($_REQUEST['openid_mode']) && $_REQUEST['openid_mode'] == 'cancel') { // User Canceled your Request
			echo "USER CANCELED REQUEST";
		}
		
		$bError = false;
		if (isset($_REQUEST['submit_openid']) && $_REQUEST['submit_openid']) {
		
			if (!func_check(getRequest('login'),'login',3,30)) {
				$this->Message_AddError('Неверный логин, допустим от 3 до 30 символов','Ошибка');
				$bError=true;
			}
			/**
			 * Проверка мыла
			 */
			if (!func_check(getRequest('mail'),'mail')) {
				$this->Message_AddError('Неверный формат e-mail','Ошибка');
				$bError=true;
			}
			/**
			 * Проверка капчи(циферки с картинки)
			 */
			if (!isset($_SESSION['captcha_keystring']) or $_SESSION['captcha_keystring']!=strtolower(getRequest('captcha'))) {
				$this->Message_AddError('Неверный код','Ошибка');
				$bError=true;
			}
			
			/**
			 * А не занят ли логин?
			 */
			if ($this->User_GetUserByLogin(getRequest('login'))) {
				$this->Message_AddError('Этот логин уже занят','Ошибка');
				$bError=true;
			}
			/**
			 * А не занято ли мыло?
			 */
			if ($this->User_GetUserByMail(getRequest('mail'))) {
				$this->Message_AddError('Этот емайл уже занят','Ошибка');
				$bError=true;
			}
			
			if (!$bError) {
				/**
				 * Создаем юзера
				 */
				$oUser=new UserEntity_User();
				$oUser->setLogin(getRequest('login'));
				$oUser->setMail(getRequest('mail'));
				$oUser->setPassword('oi');
				$oUser->setOpenid($_SESSION['openidInfo']['ident']);
				$oUser->setDateRegister(date("Y-m-d H:i:s"));
				$oUser->setIpRegister(func_getIp());
				
			
				if (USER_USE_ACTIVATION) {
					$oUser->setActivate(0);
					$oUser->setActivateKey(md5(func_generator().time()));
				} else {
					$oUser->setActivate(1);
					$oUser->setActivateKey(null);
				}
				/**
				 * Регистрируем
				 */
				if ($this->User_Add($oUser)) {
					/**
					 * Убиваем каптчу
					 */
					unset($_SESSION['captcha_keystring']);
					/**
					 * Создаем персональный блог
					 */
					$this->Blog_CreatePersonalBlog($oUser);

					/**
					 * Если юзер зарегистрировался по приглашению то обновляем инвайт
					 */
					if (USER_USE_INVITE and $oInvite=$this->User_GetInviteByCode($this->GetInviteRegister())) {
						$oInvite->setUserToId($oUser->getId());
						$oInvite->setDateUsed(date("Y-m-d H:i:s"));
						$oInvite->setUsed(1);
						$this->User_UpdateInvite($oInvite);
					}
					/**
					 * Если стоит регистрация с активацией то проводим её
					 */
					if (USER_USE_ACTIVATION) {
						/**
						 * Отправляем на мыло письмо о подтверждении регистрации						 
						 */					
						$this->Notify_SendRegistrationActivate($oUser,getRequest('password'));
						func_header_location(DIR_WEB_ROOT.'/registration/confirm/');						
					} else {
						$this->Notify_SendRegistration($oUser,getRequest('password'));
					}
					
					// логинимся
					$oUser = $this->User_GetUserByOpenid($_SESSION['openidInfo']['ident']);
					$this->User_Authorization($oUser);
					func_header_location(DIR_WEB_ROOT . '/');
					
					//func_header_location(DIR_WEB_ROOT.'/registration/ok/');			
				} else {
					$this->Message_AddErrorSingle('Возникли технические неполадки при регистрации, пожалуйста повторите регистрацию позже.','Внутреняя ошибка');
					return Router::Action('error'); 
				}
			}
		}
	}

В мелоде RegisterEvent дописывам
$this->AddEvent('openid','EventOpenid');
В какое именно место — без разницы )

В ActionLogin::EventLogin() в начало дописываем

require DIR_SERVER_ROOT . '/classes/lib/external/class.openid.php';
		if (isset($_REQUEST['openid']) && strlen($_REQUEST['openid']) > 3) {
		
			// очищаем $_REQUEST['openid']: http://shit.example.com/ => shit.example.com
			$oid = str_replace('http://', '', trim($_REQUEST['openid']));
			if ($oid{strlen($oid) - 1} == '/') {
				$oid = substr($oid, 0, strlen($oid) - 1);
			}
			
			$openid = new SimpleOpenID;
			$openid->SetIdentity($oid);
			$openid->SetTrustRoot(DIR_WEB_ROOT . '');
			$openid->SetRequiredFields(array('email','fullname'));
			$openid->SetOptionalFields(array('dob','gender','postcode','country','language','timezone'));
			if ($openid->GetOpenIDServer()){
				$oi = $openid->GetIdentity();
				//$oUser = $this->User_GetUserByOpenid($oi);
				//var_dump($oUser);
				if ($oUser = $this->User_GetUserByOpenid($oi)) {
					$this->User_Authorization($oUser);
					
					if (isset($_SERVER['HTTP_REFERER'])) {
						$sBackUrl=$_SERVER['HTTP_REFERER'];
						if (strpos($sBackUrl,DIR_WEB_ROOT.'/login')===false) {
							func_header_location($sBackUrl);
						}
					}
					func_header_location(DIR_WEB_ROOT . '/');
					
				} else {
					$_SESSION['openid_orig'] = $oid;
					$openid->SetApprovedURL(DIR_WEB_ROOT . '/registration/openid');  	
					$openid->Redirect();
				}
			}else{
				$error = $openid->GetError();
				$this->Message_AddError($error['description'],'Ошибка');
			}
			
		} else if (isset($_REQUEST['openid_mode'])) {
			$openid = new SimpleOpenID;
			$openid->SetIdentity($_REQUEST['openid_identity']);
			$openid_validation_result = $openid->ValidateWithServer();
			if ($openid_validation_result == true) { 		// OK HERE KEY IS VALID
				
			} else if($openid->IsError() == true) {			// ON THE WAY, WE GOT SOME ERROR
				$error = $openid->GetError();
				echo "ERROR CODE: " . $error['code'] . "
";
				echo "ERROR DESCRIPTION: " . $error['description'] . "
";
				$this->Message_AddError($error['description'],'Ошибка');
			} else {											// Signature Verification Failed
				echo "INVALID AUTHORIZATION";
			}
		} else if (isset($_REQUEST['openid_mode']) && $_REQUEST['openid_mode'] == 'cancel') { // User Canceled your Request
			echo "USER CANCELED REQUEST";
		}


Дальше идет строчка «if (isset($_REQUEST['submit_login']) && !$this->User_IsAuthorization()) {» — не помню менял её или нет.
Теперь правим модельку юзера
В файле
classes/modules/user/User.class.php
добавляем метод

public function GetUserByOpenid($sOpenid) {
	$data = $this->oMapper->GetUserByOpenid($sOpenid);
	return $data;		
}

В
classes/modules/user/mapper/User.mapper.class.php
дописываем

public function GetUserByOpenid($sKey) {
	$sql = "SELECT 
			u.*,
			IF(ua.user_id IS NULL,0,1) as user_is_administrator 
			FROM 
				".DB_TABLE_USER." as u
				LEFT JOIN ".DB_TABLE_USER_ADMINISTRATOR." AS ua ON u.user_id=ua.user_id
			WHERE 
				u.user_openid = ? ";
				
	if ($aRow=$this->oDb->selectRow($sql,$sKey)) {
		return new UserEntity_User($aRow);
	}
	return null;
}

32 комментария

avatar
Спасибо, нужная вещь, обязательно после нового года прикручу и попробую.
Единственные минусы в твоей работе, что многие комментарии и сообщения об ошибках пишутся на английском.
  • gran
  • 0
avatar
Под кат ;)
avatar

решаем эту проблему. че-то не ставится
echo "ERROR CODE: " — такие ошибки не разу не появлялись у меня в процессе тестирования
avatar
пожалуйста спрячьте под кат. Я замучился крутить всю страницу
avatar
логгинг ошибок эхой — фиговая идея.

из $_REQUEST[] брать чтолибо опасно — мало ли что нам туда подсунули — в апи ядра двигательного есть функция getRequest().
avatar
Вообще, я так понимаю, это код откуда-то? вставленный в ЛС?
Просветите меня, OpenID яндекса не работает в ЖЖ и наоборот, или не так?
avatar
ага, есть такое.

OpenID яндексовый какой-то особенный — пробовал прикрутить опенид к ЖУ тоже, дак используемая библиотека работала со всеми имеющимися провайдерами, кроме яндекса.
avatar
У меня с яндексом работает, тестировал на нем
avatar
в твоем коде другая либа используется для работы с опенид
avatar
Возможно, хот не должно. http://narod.ru/disk/4705805000/class.openid.php.html вот файл который у меня используется
avatar
я вспомнил: в одном из подкастов, где участвуют яндексовые сотрудники, их зам. начальника отдела разработки коммуникационных сервисов говорил, что они наконец-то починили ОпенИд и он со всеми нормально стал работать.

а было это не больше месяца назад
avatar
bobuk?
avatar
ага
avatar
перезалейте класс ещё раз пожалуйста!
avatar
Люди, кто-то реализовал у себя OpenID? дайте ссылку, плз.
avatar
fiva под тег CUT спряч код, ато простыня на главной не айс
  • xRay
  • 0
avatar
Подскажите пжл.
Вот прикрутили опенид к сайту.
Ввожу логин(жж), на сайте соглашаюсь довериться серверу, регистрируюсь
всё замечательно работает, но если любой человек с любого компа в авторизации опенид у меня на сайте введет мой жж, то также прекрасно авторизуется.

Может я чего не понимаю, но я представлял себе опенид так:
Браузер, если в окне авторизован в жж(например) или любой системе опенид, то в соседнем окне ты прекрасно авторизуешься по ссылке опенид(ник.livejournal.com)
Если ты в жж неавторизован, то при вводе на сайте опенид логина(ник.livejournal.com) тебя перебрасывает на жж и говорит сначала авторизоваться там.
ОпенИд должен так работать или нет?
avatar
Класс! Надеюсь код работоспособный? Буду пробовать,… =)

Однозначно в фаворитс.
avatar
У меня этот код не работает, а тема то очень даже актуальна.., реализовать необходимо )
avatar
Привет тебе, человек с похожим ником.

Наверняка проблема в том, что хак актуален для 0,2-ой версии, а пытаетесь ставить на 0,3.
avatar
и тебе привет ), да скорее всего я так уже и понял старенький хак.., ищу реализацию на новую версию
на хабре пост увидел habrahabr.ru/blogs/webdev/67774/
там показана реализация на другом сайте и другом движке, вполне неплохая вещь… )
avatar
кстати OpenID 2.0 никто не реализовывал под 0.3 ??
avatar
Пусть лучше прикрутят к livestreet 0.4
avatar
Ошибка после ввода openid. Потом подтверждение и переходит на сайт с 404 ошибкой. Авторизация в итоге не произведена.

Notice: Undefined offset: 0 in Z:\home\***.su\www\classes\lib\external\class.openid.php on line 245
[Денвер: показать возможную причину ошибки]
Notice: Undefined variable: query in Z:\home\***.su\www\classes\lib\external\class.openid.php on line 175
avatar
а урл такой

http://***.ru/registration/openid?openid.assoc_handle={HMAC-SHA1}{4b13c059}{5cPIbg%3D%3D}&openid.identity=http%3A%2F%2Fopenid.yandex.ru%2F****%2F&openid.mode=id_res&openid.op_endpoint=http%3A%2F%2Fopenid.yandex.ru%2Fserver%2F&openid.response_nonce=2009-11-30T12%3A53%3A45ZNHsOcc&openid.return_to=http%3A%2F%2F***.ru%2Fregistration%2Fopenid&openid.sig=Ktr%2BOw%2FfLRFm9Utso0ClFlJI7MY%3D&openid.signed=assoc_handle%2Cidentity%2Cmode%2Cop_endpoint%2Cresponse_nonce%2Creturn_to%2Csigned 


**** логин
*** домен
avatar
ну неужели никто не настраивал у себя это? Хелп ми плиз!!!
avatar
Смотрю тема популярна. Я потратил на этот код дня 2-3, так-что вполне реально, наверно с текущей версией не работает, я писал год назад.

Там все достаточно просто… щас нет ни времени ни желания этим заниматься, плюс у меня появилась аллергия на php, вобщем удачи!
  • fiva
  • +1
avatar
Насколько я знаю, есть штука наподобие OpenID от ВКонтакте. Только сайты, с этим работающие, должны быть одобрены самим ВКонтакте. Вот сайт: userapi.com
avatar
Нет, вы заблуждаетесь! Вконтакте не имеет ничего общего с интернетом и темболее с OpenID
avatar
userapi.com/?act=doc#authorization
Вконтакте не имеет ничего общего с интернетом
сарказм?
userapi.com, durov.ru, vk.ru и vkontakte.ru — все они из одной «ВКонтактовской» «секты» :)
avatar
Вобще там используется что-то типа oauth и авторизации на гугловских сервисах youtube, google groups…
avatar
Дождались:
vkontakte.ru/pages.php?o=-1&p=Open%20API

Open API — система для разработчиков сторонних сайтов, которая предоставляет им возможность авторизовывать пользователей ВКонтакте на своих сайтах и с их согласия пользователей получать доступ к их информации, друзьям, фотографиям, аудиозаписям, видеороликам, объявлениям и др.

В рамках подключения Вашего сайта к Open API создается приложение, которое будет иметь доступ ко всем текущим методам ВКонтакте API непосредственно на Вашем сайте. Помимо этого Open API предоставляет возможность упростить процесс регистрации новых пользователей на Вашем сайте, если у них уже есть учетная запись ВКонтакте.
Только зарегистрированные и авторизованные пользователи могут оставлять комментарии.