Начну так. Соединение из OpenID и hCard — это дикий фонтанирующий рулез! Это чертово будущее онлайн-регистрации, которое все обязаны реализовать в своих продуктах прямо сейчас!!!
Вниманию непрограммирующей публики!
Итак, в Cicero появилась возможность логиниться с помощью OpenID. Что в первую очередь означает, что:
- для этого форума никому не нужен будет еще один отдельный логин и пароль
- юзеры смогут использовать любые имена, которые им нравятся, и никогда не встретят сообщение "ваш ник уже занят"
- хоть это и не следует напрямую из OpenID, но я постарался сделать так, чтобы не было никакого процесса отдельной начальной регистрации
Теперь я приглашаю всех это попробовать, потестировать. Для чего вам понадобится ваш собственный OpenID. Это просто.
Во-первых, OpenID у вас может уже быть:
- адрес вашего ЖЖ (http://username.livejournal.com)
- адрес профиля на MoiKrug (http://username.moikrug.ru)
- сдается, что вскоре то же самое будет на toodoo.ru, хотя кажется сейчас это в стадии отладки
- ваш блог или какой-то другой сервис, про который вы уже сами знаете
Однако не спешите с ним сразу на форум, чтобы основной кайф не пропустить — чуть ниже будет часть про hCard.
Если же у вас такого OpenID еще нет, то его можно завести. Даже нужно, потому что, поверьте мне наслово, через полгодика это будет распространенной и полезной вещью. Поскольку OpenID — это по сути некий ваш персональный URL, то неплохо бы выбрать его красиво. Здесь на выбор есть три варианта:
Выбрать OpenID-провайдера из списка и зарегистрироваться у того, кто больше понравился. Это самый простой способ. Минусов два: вы не можете выбрать URL совершенно свободно, и вы доверяете провайдеру информацию о том, куда будете логиниться (не то чтобы это многих волновало, но есть люди, которых волнует).
Если у вас есть собственный сайт (любой), вы можете зарегистрировать аккаунт у OpenID-провайдера, но прописать его на странице собственного сайт, и для авторизации использовать свой собственный красивый URL.
Если у вас есть собственный сайт и чуть больше времени, вы можете настроить свой собственный OpenID-сервер. Сделать это можно довольно просто с помощью плагина к WordPress, или различных библиотек (и это не всеобъемлющие ссылки, поищите, сейчас этого добра очень много). Этот вариант, помимо красивого URL'а, решает еще проблему парано... э-э-э... приватности: только ваш собственный сервер будет знать, куда вы ходите.
Второй вариант представляется мне наилучшим сочетанием простоты и получаемого эффекта, поэтому я его всем и рекомендую. Делается это так:
Регистрируется OpenID, пусть это будет "http://username.videntity.org/"
Идем на эту страницу, смотрим в исходник и находим там в head'е такую строчку:
<link rel="openid.server" href="http://videntity.org/server">
Это указатель на собственно механизм (скрипт), который проводит авторизацию.
Дописываем в шаблон вашей страницы эту строчку, а также добавляем еще одну, которая скажет сервису, что ваша страница действует от имени зарегистрированного у них аккаунта:
<link rel="openid.server" href="http://videntity.org/server"> <link rel="openid.delegate" href="http://username.videntity.org/">
Это все. Теперь у вас есть страница с OpenID. Но перед тем, как его опробовать, давайте сделаем еще одну маленькую вещь (это по желанию).
hCard
OpenID — это классно и все такое, но это только часть фана. Было бы слишком скучно, если бы все люди именовались URL'ами, вас ведь не "http://.../" зовут, да? Однако этот URL — ваш персональный идентификатор — указывает на страницу. И предполагается, что на этой странице самое место, чтобы написать что-нибудь про себя. Например я в качестве своего OpenID использую вот эту страницу: http://softwaremaniacs.org/about/.
Я подумал, чтобы было бы замечательно, если бы форум мог с этой страницы сам прочитать, как вас зовут, запомнить это, и использовать в качестве вашего ника. Именно для таких вещей и служит микроформат hCard. Его используют на OpenID-страницах некоторые публичные провайдеры, например MoiKrug.ru и Videntity.org, поэтому если вы зарегистрируетесь у них, то ничего лишнего делать не нужно. Если же вы используете собственную страницу, как я описал выше, то в ней тоже нужно сделать hCard. Потому что это а) дико просто и б) не изменит внешний вид страницы.
Опуская подробности (которые можно найти в документации hCard), вот шаги по превращению вашей личной страницы в машиночитаемую карточку:
Найти на странице блок, в котором лежат ваши персональные данные. Неважно, что это: какой-то
<div>
,<address>
,<table>
или вообще весь<body>
, выбирайте по логике, как вам удобней.Добавить в этот тег
class="vcard"
. Если там уже есть какой-то класс, допишите "vcard" через пробелclass="something vcard"
.Найти, где написаны ваши имя с фамилией. Они могут уже лежать внутри какого-нибудь элемента —
<h2>Иван Сагалаев</h2>
. А могут быть просто написаны вольным текстом, тогда заверните это в span:<span>Вася Пупкин</span>
.Добавить в этот тег
class="fn"
.
Это все. Хотя, если на этой странице еще где-то присутствует ник, то его можно оформить тоже: <span class="nickname">...</span>
.
Вот теперь можно идти в форум и логиниться, он должен автоматически подхватить, как вас зовут.
Остальная часть статьи — смачные подробности реализации.
Bzr-репозиторий | http://softwaremaniacs.org/code/cicero/ |
---|---|
Работающий форум | http://softwaremaniacs.org/forum/ |
Библиотеки
Вся история с OpenID+hCard сделала Cicero сложнее в установке, потому что теперь форум использует кучу дополнительных библиотек:
py-openid (http://www.openidenabled.com/openid/libraries/python/) — это библиотека, реализующая собственно протокол OpenID. И она в свою очередь использует еще пару других, поэтому скачивайте архив "все включено", который там есть.
ElementTree (http://effbot.org/zone/element-index.htm), включена в стандартную библиотеку Python 2.5. Очень распространенная библиотека для парсинга XML.
BeautifulSoup (http://www.crummy.com/software/BeautifulSoup/). Не менее распространенная библиотека надежного парсинга HTML.
Кстати, если у вас Ubuntu 6.06, то не ставьте python-beautifulsoup из пакетов, там она слишком старая, поставьте с сайта. В Ubuntu Edgy уже новая.
Кроме того, нужно прописать две настройки, что подробно написано в файлике INSTALL в дистрибутиве. Но на самом деле, ничего особенно дикого там нет.
Авторизация (то есть аутентификация)
Признаюсь, я думал, что приблизительно зная принцип работы протокола, реализовать OpenID-клиента будет проще... Оказалось же, что это и в принципе не очень тривиально, да и с внятными описаниями и примерами проблема. Я нашел только страничку в Django-wiki да пример внутри самой py-openid (директория examples в исходном архиве). С первым проблема в том, что оно во-первых написано под старую версию библиотеки, а во-вторых там по ходу дела меняются названия и суть переменных в разных кусочках кода, что не способствует ясности :-). Официальный же пример очень трудно читать, потому что там помимо собственно авторизации еще в том же файле реализовано целиком веб-приложение вместе с WSGI-хэндлером, обработкой кучи ошибок и своей системой сессиий. Эдакий мини-фреймворк, который где-то там внутри заодно и OpenID занимается. Поэтому я постараюсь написать все подробно и пошагово.
Итак, суть OpenID-клиента в Django сводится к таким шагам:
Показать формочку с полем ввода OpenID
Принять POST из формы, отвалидировать его:
- посмотреть, что введен URL (это делается средствами Django)
- вытянуть с этого URL данные об OpenID-провайдере (это делается одним вызовом в библиотеку)
Выдать пользователю в браузер редирект на его OpenID-провайдера, напихав в этот URL всякую временную информацию для авторизации и того, что делать после авторизации (URL создается функцией из библиотеки).
Дальше пользователь сам разбирается со своим провайдером, что в лучшем случае происходит для него незаметно, если он туда уже залогинен.
Сервер провайдера присылает обратно в Django-приложение подтверждение авторизации вместе с той кучей данных, которые мы присылали туда в шаге 3.
Проверить присланное на соответствие посланному (еще один вызов в библиотеку).
Если все хорошо, достать или создать джанговского пользователя, соответствующего OpenID URL'у и зафиксировать его в кукесах стандартными средствами Django.
Сложная часть в том, где это все в коде расположить. Я видел несколько примеров джанговского OpenID'шного кода, где все это делалось внутри двух (а то и одной) view-функций, совершенно игнорируя хорошие джанговские механизмы. Поэтому я решил все сделать правильно (то есть, как мне кажется правильным :-) ).
Начало
Первые 2 шага — это практическии стандартная view, обрабатывающая форму: на GET форма показывается, на POST — валидируется и сохраняется. Разница только в том, что вместо сохранения объекта успешным выходом из формы будет URL редиректа пользователя к своему провайдеру. Из-за этой особенности view придется написать вручную, а не пользоваться generic'ами:
def login(request):
from cicero.forms import AuthForm
if request.method == 'POST':
form = AuthForm(request.session, request.POST)
if form.is_valid():
after_auth_redirect = form.auth_redirect(post_redirect(request))
return HttpResponseRedirect(after_auth_redirect)
else:
form = AuthForm(request.session)
return render_to_response(request, 'cicero/login.html', {'form': form, 'redirect': post_redirect(request)})
Я считаю очень важным, чтобы view-функции обработки форм строились по одному паттерну: сама view занимается фактически только HTTP-диспетчеризацией, а основная работа — будь это сохранение объекта, отсылка email'а или проверка OpenID URL'а — должна делаться внутри объекта формы. Хотя в примерах я встречал, например, такую вещь:
def login(request):
if request.method == 'POST':
form = AuthForm(request.POST)
if form.is_valid():
try:
# проверка OpenID URL'а
except Ошибка1:
form.errors[...].append('...')
except Ошибка2:
form.errors[...].append('...')
То есть по сути единый процесс проверки ввода — валидация — разделяется зачем-то на два и лежит в двух местах: стандартная валидация, а затем код, который пытается ее извне дополнять. Выглядит криво. Поэтому я запихнул это все в форму в стандартное место валидации поля с URL'ом:
class AuthForm(Form):
openid = URLField(label='OpenID', required=True)
# ...
def clean_openid(self):
from cicero.auth import get_consumer
from yadis.discover import DiscoveryFailure
from urljr.fetchers import HTTPFetchingError
consumer = get_consumer(self.session)
errors = []
try:
self.request = consumer.begin(self.clean_data['openid'])
except HTTPFetchingError, e:
errors.append(str(e.why))
except DiscoveryFailure, e:
errors.append(str(e[0]))
if hasattr(self, 'request') and self.request is None:
errors.append('OpenID сервис не найден')
if errors:
raise ValidationError(errors)
Как видно, форма состоит из одного поля. Его тип (URLField) обеспечивает самую базовую валидацию — похоже ли оно на URL вообще. Чтобы дополнить ее своей, надо в форме определить метод вида "clean_названиеполя", который Django и вызовет. Весь метод строится вокруг одного вызова клиентского объекта OpenID-библиотеки — "consumer.begin", который обращается по переданному URL'у и проводит всю черновую работу: запрашивает дремучие XML-описания, сверяет версии, парсит HTML на предмет <link rel="openid.server" ...>
и все такое. Если что-то идет не так, выкидывает исключения, которые тут же сваливаются в список найденных ошибок. Если ошибок нет, то в форме создается поле request, которое будет использоваться дальше для построения редиректа.
Кстати о "клиентском объекте"... У библиотеки есть две части — server и consumer (зачем было client'а называть consumer'ом?). Клиент создается не очень тривиально, почему и вынесен в отдельную функцию get_consumer:
from openid.consumer.consumer import Consumer
from openid.store.filestore import FileOpenIDStore
from django.conf import settings
def get_consumer(session):
if not settings.OPENID_STORE_ROOT:
raise Exception('OPENID_STORE_ROOT is not set')
return Consumer(session, FileOpenIDStore(settings.OPENID_STORE_ROOT))
Ему для существования нужны две вещи. Во-первых, некий объект сессий, на роль которого удачно подходят джанговские сессии, и которые и передаются туда (из вьюхи login в AuthForm, а из нее — в get_consumer). А во-вторых, специальный объект-склад, где библиотека будет хранить временные данные авторизации, и который вынуждает таки требовать отдельной настройки при установке Cicero, потому что директорию на локальном диске придумать "по умолчанию" не получится...
Есть там, правда, еще два способа организации склада. Один — хранение в БД — мне не понравился, что ли, эстетически: лишние таблицы выглядят большим злом, чем директория. А второй — некий "тупой" режим, для которого собственно хранения не требуется, но он считается несекьюрным, и что главное, я не смог заставить его работать :-). Если у кого получится, напишите!
Итак, если форма выдает ошибки, они показываются — тут все стандартно. Если ошибок нет, то из формы надо получить URL провайдера, на который отправлять редирект браузеру, для чего в форме вместо метода "save()" есть метод "auth_redirect()", который его "конструирует". Другого слова и не подобрать, потому что процесс сложнее, чем кажется:
class AuthForm(Form):
# ...
def _site_url(self):
from django.contrib.sites.models import Site
site = Site.objects.get_current()
return 'http://' + site.domain
def auth_redirect(self, target):
from django.core.urlresolvers import reverse
site_url = self._site_url()
trust_url = settings.OPENID_TRUST_URL or (site_url + '/')
return_to = site_url + reverse('cicero.views.auth')
self.request.return_to_args['redirect'] = target
return self.request.redirectURL(trust_url, return_to)
URL конструирует объект request, который получается из библиотеки во время опроса провайдера. В него заворачиваются два параметра, которые провайдеру нужны:
"trust_url" — чисто информационная штука, название сайта, которое будет показано пользователю форума при авторизации на странице его провайдера в духе: "Хотите ли вы действительно доверить свою авторизацию сайту такому-то". Как видно из кода, это можно указать в настройках, но по умолчанию берется весь сайт. Я исхожу из предположения, что весь сайт работает на Django, и OpenID-авторизация на форуме как раз залогинит пользователя на весь сайт.
"return_to" — URL, на который провайдер перекинет пользователя для завершения авторизации. Чтобы не зашивать этот URL в код, он получается из джанговской функции reverse, которая ищет в urlconf реальный адрес, который показывает на view, занимающуюся этой авторизацией.
В обоих параметрах используется site_url — корень сайта. В принципе, его можно достать из переменной запроса "HTTP_HOST", но в Django так не принято. Для этого существует встроенное (и, кажется, ставящееся автоматически) приложение Sites, которое создает в базе табличку с двумя полями: красивым названием текущего сайта и его доменом. Хорошо это например тем, что приучает пользователей использовать один канонический URL сайта, а не разные варианты (с "www.", без "www." и т.д.).
И последний важный параметр, который передается в URL — target. Это то, куда надо будет отправлять пользователя уже после авторизации. Что странно, этот "маловажный" момент пропускают все как один примеры авторизации по OpenID, говоря, что "теперь пользователь авторизован, делайте с ним все, что вам нравится". В традиционных схемах это не проблема, потому что авторизация происходит сразу в том месте, где обрабатывается первая форма, и туда можно передать какой-нибудь параметр "redirect" или использовать реферер. В OpenID этот параметр приходится гонять по всему пути через провайдера.
Завершение
Сама view, которая принимает редирект от провайдера, очень простая, практически из документации:
def auth(request):
from django.contrib.auth import authenticate, login
user = authenticate(session=request.session, query=request.GET)
if not user:
return HttpResponseForbidden('Ошибка авторизации')
login(request, user)
return HttpResponseRedirect(request.GET.get('redirect', '/'))
А всю работу делают функции authenticate и login из джанговской системы аутентификации. Они занимаются непосредственно опознанием и запоминанием пользователя. Причем опознание делается через подключаемые в настройках бэкенды — классы, которые реализуют разные виды аутентификации. Соответственно для OpenID тоже нужен свой бэкенд. Лежит он в отдельном файле auth.py и выглядит примерно так:
class OpenIdBackend(object):
def authenticate(self, session=None, query=None):
# проверить данные, присланные удаленным сервером,
# соответствуют ли они посланным в начале процесса
# в query передаются параметры GET из запроса
consumer = get_consumer(session)
info = consumer.complete(query)
# если нет, вернуть None -- стандартное поведение для backend'а
if info.status != SUCCESS:
return None
from cicero.models import Profile
try:
# найти пользователя, если такой OpenID уже есть в его профиле
profile = Profile.objects.get(openid=info.identity_url)
user = profile.user
except Profile.DoesNotExist:
# если нет, создать пользователя и профиль
import md5
unique = md5.new(info.identity_url).hexdigest()[:23] # 30 - len('cicero_')
user = User.objects.create_user('cicero_%s' % unique, 'user@cicero', User.objects.make_random_password())
profile = user.cicero_profile
profile.openid = info.identity_url
profile.save()
return user
Надо заметить, что несмотря на то, что решение об успешной аутентификации принимается OpenID-библиотекой, и джанговская таблица пользователей тут не участвует, записи в ней все равно создаются. Так рекомендуется делать, потому что в Django есть много полезных сервисов, завязаных именно на эту таблицу пользователей: права, сообщения на сайте, админка.
Для OpenID пришлось также расширить профиль на два поля, потому что встроенной таблицы User не хватает:
Имя (username) в ней уникально, а я хотел сделать так, чтобы имя человек мог выбирать свободно. Поэтому в Profile добавляется поле "name".
Сам OpenID тоже хранится в профиле.
Из-за появления отдельного понятия имени пользователя пришлось переписать его отображение (раньше для него использовался User.username). Теперь оно возвращает Profile.name, если его нет — Profile.openid, если его нет — User.username, который есть всегда. Выглядит красиво, но к сожалению добавляет один или даже два отдельных SQL-запроса на каждую статью при выводе топика! Пока я оставил это так, но надо будет исправить.
Выемка Profile.name
На закуску осталась автоматическая выемка имени пользователя с OpenID-страницы. Делается это в момент сохранения профиля при условии, что OpenID в нем есть, а name — нет. Принцип работы прост: вытянуть файл по URL'у, распарсить HTML в поисках элемента с классом "vcard", внутри него найти элементы с классами "nickname" или "fn" ("ник" и "имя", соответственно), записать их содержимое в name.
Для парсинга HTML используется библиотечка BeautifulSoup, и выглядит он действительно просто и красиво:
import re
from BeautifulSoup import BeautifulSoup
soup = BeautifulSoup(content)
vcard = soup.find(None, {'class': re.compile(r'\bvcard\b')})
В следующий раз, когда вам скажут, что HTML парсить дико трудно, а XML дико просто, вспоминайте о том, что этот вопрос надо решать не вам лично, а библиотекам. Скорость же этого парсинга в данном случае не играет почти никакой роли (этот парсинг все равно сильно быстрее собственно загрузки страницы), как и во многих других случаях.
Дальше аналогичным образом внутри vcard
ищутся элементы ника или имени. Здесь надо учитывать интересный паттерн, сложившийся в микроформатах. Обычно элемент с нужным классом содержит данные внутри себя:
<span class="nickname">Maniac</span>
Но если этот элемент — <abbr>
, то внутри него написано некое "сокращенное" представление данных, а развернутое переносится в атрибут "title":
<abbr class="fn" title="Иван Сагалаев">Иван С.</abbr>
Для обработки таких случаев пришлось написать маленькую подфункцию:
def _parse_property(class_name):
el = vcard.find(None, {'class': re.compile(r'\b%s\b' % class_name)})
if el is None:
return
if el.name == u'abbr' and el['title']:
return el['title'].strip().encode(settings.DEFAULT_CHARSET)
else:
result = ''.join([s for s in el.recursiveChildGenerator() if isinstance(s, unicode)])
return result.strip().encode(settings.DEFAULT_CHARSET)
Пока на этом я остановился. Следующее, что предстоит сделать — это визуально разделить гостей и пользователей, имеющих OpenID. И там я обещаю кое-что интересное :-).
Комментарии: 39
Иван, это очень жирный пост!
Большое спасибо что не ленишься писать так все подробно и ясно.
Спасибо огромное, просто и понятно о OpenID. И сам буду теперь его юзать ;)
Понравилось =)))
Пост еще не дочитал, времени нет. Но вот вопрос есть, чисто технический, - ты говоришь, что OpenID сервер модет быть установлен на любом серваке. Так вот - а как все сервера взаимодействуют между собой?
Приведу пример, если не сильно понятно: ты используешь в качестве сервера videntity.org, у меня есть свой OpenID сервер, но он установлен на моем сервере и к videntity.org я никакого отношения не имею. На каких правах я смогу по своему url'у зайти на твой форум, если никто, кроме моего OpenID-сервера об этом url'е не знает.
PS. А почему у тебя в форума никаких rel="openid.*" нет?
Вопросы сняты :)
Огромное спасибо за обстоятельный и полезный рассказ о технологии!
А теперь - ложка дегтя :)
Все это очевидный пример, когда удобное для реализации вступает в противоречие с потребностями пользователя.
Достаточно большому количеству людей нужны МНОГО (больше одного то есть) аккаунтов, более того - никак не связанных друг с другом. Это ведь еще и прайваси, в конце концов.
Действительно, спасибо, информация очень полезная. я вот, вообще, как то краем уха про это слышал, после прочтения решил прикрутить везде у себя :)
WiRED, помимо того, что в Cicero остается возможность гостевого постинга, еще и в принципе никто не мешает человеку иметь несколько OpenID, почему нет? Смысл в том, что тут решение о том, сколько плодить логинов, человек принимает сам.
В смысле спамерства это не особенно страшно, потому что потребителей рекомендуется трактовать любой новый логин как "недоверенный", и только если от него идет нормальный контент, включать его в "белый список".
Класс! Нужно будет непременно заюзать!
Вот я одного не понимаю в авторизации по OpenId: что мешает злоумышленнику создать сервер, который быдет идентифицировать всех подряд, т.е. всегда говорить "да, этот пользователь тут есть!"
Отлично! Теперь осталось сделать openid-аутентификатор для самой джанги.
Тимур, я как раз коснулся этого чуть выше в комментарии про белый список. Действительно, проверка OpenID не означает для сервиса, что пользователю можно доверять. Доверие строится потом, на том основании, что владелец OpenID пишет нормальный контент и не дает пользоваться своим OpenID спамерам.
То есть эта регистрация не дает никакой дополнительной защиты сервисам относительно того, что есть сейчас. Она в этом смысле не отличается от посылки подтверждения на email: ведь email'ов тоже можно регистрировать сколько угодно.
Смысл OpenID в основном в удобстве для пользователей.
Одна небольшая проблема.
К тому же, валидным OpenId-идентификатором может быть не только URL, а также XRI, например, iname (выглядит как =myiname). Причем в последнем случае меняется механизм аутентификации, который ресолвит i-name в URL проверки юзера у его регистратора, и отправляет клиента туда.
Библиотека сама проверяет валидность и тип идентификатора, сама добавляет все что нужно.
UrlField же требует, чтобы приехал URL по полной схеме. Так что тут лучше обойтись простым CharField, и всю валидацию делегировать библиотеке, она в данном случае лучше знает.
Анонимных провайдеров можно просто вычислить - при вводе нового идентификатора ("регистрации") попробовать сначала авторизоваться у провайдера с каким-нибудь левым именем, которого быть уж точто не должно. Например, md5-хешем от случайного числа :)
Максим, оба исправления сделал, спасибо!
Правда только:
Доверяй, но проверяй :-). На самом деле "openid_url": http://openid.net/specs/openid-authentication-1_1.html#anchor6 (там в пункте 3.2.1 последний буллит).
В спецификации 2.0 - openid_identifier
Видимо, чтобы поставить ударение на присутствие не-URL-идентификаторов :)
http://openid.net/specs/openid-authentication-2_0-11.html#initiation
Ах, 2.0... Но она пока еще proposed draft :-). Причем я думаю, что это как раз та вещь, которая действительно может поменяться, потому что обратная совместимость здесь реально полезней теоретической чистоты. Кстати, на http://www.livejournal.com/openid/ — тоже openid_url.
отлично, работает и читает hCard
вот только с визуальной точки зрения пользователи не выделяются :
т.е. нельзя различить, например, полных тёзок :)
Хм. Мне, по какой-то неизвестной причине, аутентифицироваться не удалось.
Иван, а можно вас попросить сделать более подробный вывод ошибок авторизации? Если у вас будет время, конечно.
Дело в том, что у меня при попытке входа Cicero ругается на "Ошибку авторизации". При этом авторизация на моем OpenID-сервере проходит удачно. На http://livejournal.com/openid/ логинюсь без проблем.
При авторизации указываю URL http://antonmorozov.ru
Скрипт сервера брал отсюда: http://siege.org/projects/phpMyID/
Большое спасибо!
Иван, крайне заинтересовался — наконец-то можно показать на практике, зачем может пригодиться hCard! Ну т.е. я это понимаю, но вот разработчиков, которых мне доводилось агитировать в пользу микроформатов, как-то не вставляла абстракция (open data, децентрализация и т.п.).
Могу ли я воспользоваться этой идеей в своём докладе о микроформатах для РИТ 2007?
Максим, я вот теперь сижу и думаю, как я сам-то не додумался это предложить :-). Конечно можно и нужно. Мне кажется, что микроформатам сейчас именно этого и не хватает — полезных реализаций.
Спасибо за описание, хотел реализовать OpenID авторизацию, но было очень мало информации.
Неплохо было бы сначала почитать переписку в почтовом листе разработчиков OpenID, чтобы понять, почему идентичное решение уже было один раз отвергнуто ;)
Суть в том, что Yadis (если не ошибся) сам может переносить данные о пользователе, так что hCard это просто дубляж возможностей OpenID
Да... Есть там такой механизм sreg. А дублировать - зачем это?
Нет, ну что значит "дублировать"? hCard появился и раньше OpenID, и раньше SRE. Так что это SRE — альтернативная технология, а не наоборот. Другое дело, что это не конкуренты. SRE решает более узкую задачу с меньшим количеством эвристики. hCard (и остальные микроформаты) — более богатая инфраструктурая, из которой можно вытянуть больше информации.
Основной смысл для OpenID-клиента читать hCard в том, что эта hCard там может быть, и юзера можно магическим образом избавить от заполнения профиля.
Очень интересная фишка.
Однако, вопрос: я попробовал запостить пост на тестовом форуме. Аккаунт использовал на moikrug.ru. Все отлично идентифицировалось. Однако, в форуме я фигурировал под своим именем и фамилией. Если же у меня есть полный тезка, то он на этом же форуме будет с такой же фамилией и таким же именем. И в случае, например, спамерства, шишки от недовольных пользователей могут посыпаться и на меня. Согласитесь, не каждый начнет разбираться с тем, с какого домена пришел данный пользователь... Мало того, если бы я не прочитал сейчас про openId, я бы подумал, что мой аккаунт взломан и кто-то использует мои данные (это, конечно, в случае, сли я неопытный пользователь). Так что вещь, конечно, замечательная, но без широкой информированности масс может вызвать различные казусы. А жаль....
Голос из-за бугра:
http://lucidplot.com/2007/07/12/microformats-kill-facebook/
хотел спросить... а не будет ли такая ситуация что кто-то может воспользоваться моим openid чтобы писать от моего имени? Это ведь не секурно... или я не прав?)
Нет, для этого человеку надо будет уметь логиниться от вашего имени в тот сайт, на котором OpenID. То есть в ваш блог, ваш ЖЖ и т.д. Если он умеет это делать, то он и так уже может от вашего имени что-то публиковать.
Целых 2 неточности в классе AuthForm: тут не описан конструктор формы (ага, конструктор получает только request.POST, а request.session нужно запихивать туда отдельно) и опечатка. Вместо
должно быть
я когда делал на обоих граблях застрявал. исправьте для будущих поколений (%
Спасибо за статью, благодаря ей только что прикрутил openid к своему блогу на django.
На данный момент (Django 1.0, python-openid 2.0.2-1, python-2.5) пришлось исправить:
1) В класс
AuthForm
добавить конструктор и изменить в функцииclean_openid
параметрself.clean_openid['openid']
наself.cleaned_openid['openid']
(как писали выше):2) Функция
complete
классаopenid.consumer.consumer.Consumer
на данный момент (версия python-openid - 2.0.2-1) принимает два параметра, помимоquery
, еще иreturn_to
- тот самый который был передан провайдеру:3) Проверка Response от провайдера (в бэкенде для аутентификации) было:
а надо:
4) В класс OpenIdBackend необходимо добавить функцию get_user
Остальное по мелочи.
Код в посте блога в любом случае не стоит воспринимать, как полностью работающий. Это примеры для иллюстрации. Работающий же код лежит в
svn://softwaremaniacs.org/cicero
, и там вообще довольно много поменялось за полтора года.Иван, скажите а для чего Вы на своей страничке http://softwaremaniacs.org/about (которая является Вашим OpenID идентфикатором) вписываете openid.delegate на тот же идентификатор?
Делегат нужен, если бы Вы хотели авторизоваться куда-либо под другим идентификатором, как я понимаю.
Это артефакт того, что ссылки на сервер и делегат прописаны в одном месте в коде, и проверять там "а не уже ли это та самая страница" было неудобно. Поэтому оно осталось так, благо не мешает.
Скажите, а что бы "запустить" использование микроформатов на странице, обязательно вставлять отдельный внешний тег с class='vcard' или можно просто добавить через пробел class='megabox vcard' к существующему тегу?
Да, достаточно добавить. Класс должен быть, но не обязан быть единственным.