英語版主体で運営しているLibroSpotに、どこの国の方か分かりませんが「OpenIDに対応してくれ!」という要望があったので、ひと肌脱ぐことにしました。
LibroSpotはGoogle App Enigne上でDjango用のヘルパー(google-app-engine-django)を利用して動いています。
OpenIDの認証を自前で実装するとなるとコトですが、
google-app-engine-django向けのOpenIDライブラリがあるので、これ幸いと活用させていただくことにしました。
google-app-engine-django-openid
http://code.google.com/p/google-app-engine-django-openid/
結論から言うと、正直OpenIDの実装方法なんて全然知りませんでしたが、小1時間で対応完了!
ただし、若干の不具合(!?)らしきものがあったのでご報告を。
1. ログイン後のリダイレクト先があやしい
サイトの作りにもよりますが、ログインしたらそれまで開いていたページに戻って来て欲しいもの。
google-app-engine-django-openid(gaedo)も当然そのような処理をくわえています、が。。。正しく動いていないような!?
openidgae/templates/openidgae-login.html
<form action="{% url openidgae.views.OpenIDStartSubmit %}?continue={{continueUrl}}" method="post">
<input type="text" name="openid_identifier" id="openid_identifier" />
<input type="submit" value="Verify" /<
</form>openidgae.views
def get_continue_url(request, default_success_url):
continueUrl = request.GET.get('continue', default_success_url)formのactionにcontinueというパラメーターを付けていますが、これがrequest.GETの中に入ってきません。
postでサブミットしているからでしょうか?
また、continueUrlという変数にも何も入っていないので、そのままでは元のページに戻ることはありません。
それをふまえて書き換えたのがこちら。
openidgae/templates/openidgae-login.html
<form action="{% url openidgae.views.OpenIDStartSubmit %}" method="post">
<input type="text" name="openid_identifier" class="standard" style="width:140px;" id="openid_identifier" />
<input type="submit" class="standard" value="{% trans 'Verify' %}" />
<input type="hidden" name="continue" value="{{ request.get_full_path }}" />
</form>openidgae.views
def get_continue_url(request, default_success_url):
if request.method == 'POST':
continueUrl = request.POST.get('continue', default_success_url)
else:
continueUrl = request.GET.get('continue', default_success_url)continueパラメーターは無難でhiddenで渡すように変更し、遷移元のパスを明示的に指定しました。
ちなみに、テンプレートでrequestオブジェクトを使うには
setting.py
TEMPLATE_CONTEXT_PROCESSORS = (
'django.core.context_processors.request',
)2. GAE付属のGoogle Accounts Python APIとの併用
認証まわりはgaedo1本でいくとしてもGoogle Accounts Python APIと併用するにしても、問題が出てきます。
gaedoはDjangoの認証まわりの各種メソッドに対応していないので、例えば、viewで request.user.is_authenticated を実行しても常にFalseが返ってきます。
また、すでにGoogle Accounts Python APIを導入済みの場合は、ユーザー情報の管理がUserモデルとPersonモデルに分割され、しかも情報が共通ではありません。
LibroSpotの場合はすでにGoogle Accounts Python APIを利用して認証を行っていたので、こちらをベースになんとかできないものかと模索したところ、これでいいのかな?という形に一応まとまりました。
いじるのは appengine_django.auth.middleware ひとつだけです。
オリジナル
class LazyUser(object):
def __get__(self, request, obj_type=None):
if not hasattr(request, '_cached_user'):
user = users.get_current_user()
if user:
request._cached_user = User.get_djangouser_for_user(user)
else:
request._cached_user = AnonymousUser()
return request._cached_user変更後
class LazyUser(object):
def __get__(self, request, obj_type=None, person=None):
if not hasattr(request, '_cached_user'):
user = users.get_current_user()
if user:
request._cached_user = User.get_djangouser_for_user(user)
else:
from django.http import HttpResponse
import openidgae
person = openidgae.get_current_person(request, HttpResponse())
if person:
request.__class__.openid_person = person
request._cached_user = self.get_djangouser_for_openiduser(person)
else:
request._cached_user = AnonymousUser()
return request._cached_user
def get_djangouser_for_openiduser(self, person):
if hasattr(person, 'user_ref') and person.user_ref:
django_user = User.get(person.user_ref)
else:
email = person.get_email()
name = person.person_name()
u = email if email else name
django_user = User(user=users.User(u), email=email, username=name)
django_user.save()
person.user_ref = django_user.key()
person.save()
return django_userこのMiddlewareはgoogle-app-engine-djangoの認証まわりのコアとなるクラスで、認証およびデータの格納を一元的に管理しているようです。
Personモデル(openidgae.models.Person)はdb.Expandoを継承しているので、後から要素を追加することが可能です。
OpenIDでログインしている場合は、新しくUserモデル(appengine_django.auth.models.User)のオブジェクトを作成し、そのKeyをPersonモデルに格納して両者をひもづけることにしました。
さらにUserオブジェクトをrequestオブジェクトにセットすることで、前述の request.user.is_authenticated などが使えるようになります。
と、まあ、上記のように自分なりにいろいろいじった部分がありますが、おおむねではもの凄く簡単にOpenID対応が可能になります。
少なくともOpenIDの認証方法には一切タッチすることなく!


0 コメント:
コメントを投稿