今回は OpenID API を使ったサンプルの最終回です。
アプリケーション(自己紹介登録モジュール)
前編 及び 中編 で OpenID認証 と OpenID認証(ID入力) を例示した。今回これらのモジュールを使った自己紹介登録アプリケーションのサンプルを示していきたい。
自己紹介登録モジュールは、ユーザ毎に名前と自己紹介文を登録・表示を行う。極めて単純なモジュールだ。
自己紹介登録モジュールの処理
- User クラスからユーザ情報を取得
- ユーザ情報からデータストアを検索し、該当するデータがある場合表示する
- 登録ボタンを押すと、画面入力した名前と自己紹介文をデータストアに格納
それではソースを示していきたい。自己紹介登録モジュールを構成するのは以下のソースである。
- モデル - models.py
- テンプレート - main.html
- プログラム - main.py
-
# -*- coding: utf-8 -*- from google.appengine.ext import db class UserData(db.Model): user = db.StringProperty(required=True) name = db.StringProperty(required=True) comment = db.TextProperty(required=True) updated = db.DateTimeProperty(auto_now=True) created = db.DateTimeProperty(auto_now_add=True)
- ソース7 解説
- UserData クラスが、データストアでのエンティティの定義である。
- user にはユーザIDが入る。Googleアカウント認証の場合は UserProperty を利用するが、OpenID(Federated Login)では UserProperty を使うと動かない。この件は後で説明する。
- name 及び comment は名前とコメントが入るプロパティである。
- updated 及び created は日時が入るプロパティである。オプションが設定してあり、created の方はエンティティを追加した日時、updated はエンティティの更新日時が自動で設定される。
-
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd"> <html> <head> <meta http-equiv="Content-Type" content="text/html; charset=utf-8"> <title>☆自己紹介☆</title> </head> <body> <form method="post" action=""> <table cellpadding=5> <tr><td colspan=2>お名前と自己紹介を登録してください。</td> <td><a href="{{ logout_url }}" target="_self">ログアウト</a></td></tr> <tr><td>お名前</td> <td><input type="text" name="name" size=40 value="{{ name|escape }}"></td></tr> <tr><td>自己紹介</td> <td><textarea rows="5" cols="40" name="comment">{{ comment|escape }}</textarea></td></tr> <tr><td>更新日時</td> <td>{{ updated }}</td></tr> <tr><td></td> <td><input type="submit" value="登録"/></td></tr> </table> </form> </body> </html>
-
ソース8 解説
- Submitボタンを押すと、Post関数が呼ばれる。
-
# -*- coding: utf-8 -*- import os from google.appengine.ext.webapp import template from google.appengine.ext import webapp from google.appengine.ext.webapp.util import run_wsgi_app from google.appengine.api import users from google.appengine.ext import db from models import UserData class EditHandler(webapp.RequestHandler): def get_user_data(self): self.current_user = users.get_current_user().user_id() query = db.Query(UserData) query.filter('user = ', self.current_user) self.user_data = query.get() def get(self): self.get_user_data() if self.user_data is not None: name = self.user_data.name comment = self.user_data.comment updated = self.user_data.updated else: name = '' comment = '' updated = '' template_values = {'logout_url': users.create_logout_url('/'), 'name': name, 'comment': comment, 'updated': updated } path = os.path.join(os.path.dirname(__file__), 'templates', 'main.html') self.response.out.write(template.render(path, template_values)) def post(self): name = self.request.get('name') comment = self.request.get('comment') self.get_user_data() if self.user_data is not None: self.user_data.name = name self.user_data.comment = comment self.user_data.put() else: UserData(user=self.current_user, name=name, comment=comment).put() self.redirect('/') def main(): run_wsgi_app(webapp.WSGIApplication([('/', EditHandler),], debug=True)) if __name__ == '__main__': main()
-
ソース9 解説
- Get関数では、認証情報からデータストアを検索した結果の表示を行っている(17行目)。
- Post関数では、画面入力した情報をデータストアに登録・更新している(32行目)。
- Get関数・Post関数とも get_user_data を呼びだしているが、get_user_data ではユーザ・インスタンスの取得及びデータストアの検索を行っている(11行目)。
- ユーザの識別には、ユーザインスタンスの user_id() メッソドを利用している(12行目)。これについても後で解説する。
アプリケーションの環境設定
まず URLパターンであるが、app.yaml 内に記述する URLハンドラー記述は次のようになる。
-
handlers: - url: /images static_dir: media/images - url: /css static_files: media/css upload: media/css/.+\.htc mime_type: text/x-component - url: /css static_dir: media/css - url: /_ah/login_required script: select_login.py - url: /login/.* script: select_login.py - url: .* script: main.py login: required
-
ソース10 解説
- /_ah/login_required (13行目)は 以前の記事 で解説した Federated Login にした場合に使用する URL である。ここに認証用のモジュールを割り当てる。
- /login/.* (16行目)は、OpenID認証(ID入力)で利用しているが、パターンは select_login.py の方で定義されている。
- .* (19行目)はアプリケーションの定義である。ここで login: required オプションを付けているため、ユーザ認証が動く。
次に開発環境における各ファイルの配置であるが、下のようになる。
OpenID認証モジュール では各種アイコンを使っている(前編)。ファイルは media\images に配置する。
参考
Vector Social Media Icons
akahoshitakuya.com - 読書メーターがOpenIDに対応しました!(mixi Yahoo はてな)OpenID認証(ID入力)モジュール ではIE用に PIE.htc を使用している(中編)。このファイルは、media\css に配置する。デザインだけの問題なので、ファイルを配置しなくても問題は無い。
参考: CSS3 PIEフォルダー構成を変更したい場合は、 app.yaml の記述を変更すればよい。
サンプル作成で気づいた点・留意点など
箇条書きに、気づいた点・留意点などを書いていく。
- セッションの使用
- 今回セッションを使用していない。必要な変数は Get・Post変数として渡しているが、これはスマートなやり方ではない。また特に、Get変数は改ざんが容易である。サンプルではプロバイダの検証を行っていないため、リストに載っていないブロバイダでもログインできる。やはりこのようなアプリケーションではセッションの利用をお勧めする。
- OpenID認証(ID入力)の入力方式
- サンプルでは汎用性を持たせるため OpenID を全て入力する形式にした(中編)。ガイダンスを表示するにしても、この方法でユーザに入力してもらうにはハードルが高い。「はてな」などは、OpenIDの一部分にプロバイダーのユーザIDを使っている。そのようなプロバイダーの場合は、ユーザIDの入力画面だけ用意して、プログラムで OpenID(URL)を生成した方が安全で快適だ。
- ユーザID を格納するプロパティ
- ユーザID をデータストアに格納するプロパティとして、文字列(StringProperty)を利用している(ソース7)。Googleアカウントの場合は UserProperty を使うが、OpenID(Federated Login)では利用できない。もし UserProperty で使った場合、このような現象 に悩まされることになる。
- ユーザID(Userインスタンス)
- データストアに格納するユーザID として、Userインスタンスの user_idメソッドを利用している(ソース9)。永続的で不変な ID を返すとされている。しかしこの ID は、どこのプロバイダのIDなのかも判別不能だ。サンプルでは行っていないがデータストアにユーザ情報を格納するときに、プロバイダ情報などを一緒に入れた方がよい。
user_idメソッドを使わずに、Userインスタンスの値を使ってデータストアに格納してもよいかもしれない。Userインスタンスが返す値は、Googleの場合は GoogleID、他のプロバイダは OpenID となっているようだ。
参考: Google Code - Userクラス
- メールアドレス・ユーザ名など
- Google Code には Userクラスに、メールアドレス・ニックネームなどのメソッドの紹介がある(Google Code - Userクラス)。これらのメッソドが値を返すのは、プロバイダが Google の場合だけだ。OpenID(Federated Login)API は現在、AX プロトコルなどに対応していない。
- Facebook, twitter, WindowsLive アカウント
- Facebook, twitter, WindowsLive は OpenIDに対応しているとアナウンスしているが、OpenID のプロバイダ・サービスは行っていないようだ。これらのアカウントを使って OpenID認証対応アプリケーションにアクセスすることはできない。
長々と記事を書いてしまい、申し訳ないです。もっとサクっと紹介できるかと思ったのですが・・・難しかったです。今後は、もうちょっと軽い内容の記事を心がけます。