2010年10月31日日曜日

OAuth Examples (Python) を読んでみる

OAuthが必要になったので勉強してます。
とりあえず、Google先生に聞いて概要は理解したけど
ソースを読まないことにはなんともならん、と。

で、

OAuth Examples

を読んでみた。
Pythonなので、
AppEngine-OAuth-Library by Mike Knapp (more)
ってところです。


OAuthの流れ。
自サイトとTwitter連携の話。

 Consumerは自サイト
 Service ProviderはTwitter
Userはあなた

twt-tnkというサイトを作りました。

これが自サイトとします。





0.ConsumerはService ProviderからあらかじめOAuth利用許可を得る

このステップは,具体的にはService ProviderにConsumer登録を行い,Consumer KeyとConsumer Secretという値を取得することで行います。
これは、事前の登録で、Twitterにそのページがあります。


開発者
連携アプリの追加と設定はこちらから変更できます。


ってところです。


1.UserがConsumerに,Service Providerから認可が必要な情報へのアクセス権を取得するように指示する。

自サイトにある「Twitterにログイン」的なリンクを用意し、それをUserが押す行為です。

twt-tnk画面上部に「Twitterアカウントでログインする 」ってのがあり、
「/login」というURLになってます。


2.ConsumerはバックグラウンドでService Providerにアクセスし,未認可のRequest Tokenを取得する

自サイトがバックグラウンドでTwitterにアクセスし、未認可のRequest Tokenを取得します。

  1. def get(self, mode=""):  
  2.   
  3.         #TwitterClient、Cookiesクラスの作成  
  4.         (client, cookie) = CreateClientAndCookie(self)  
  5.   
  6.         #Twitterの認証画面表示  
  7.         if mode == "login":  
  8.             #Twitterの認証画面へリダイレクトする  
  9.             return self.redirect(client.get_authorization_url())  
  10.   
  11. def CreateClientAndCookie(self):  
  12.   
  13.     #Twitter認証画面からのコールバック用URLを設定  
  14.     callback_url = "%s/verify" % self.request.host_url  
  15.   
  16.     #TwitterClientクラスの作成  
  17.     client = oauth.TwitterClient(CONSUMER_KEY, CONSUMER_SECRET,  
  18.         callback_url)  
  19.     #Cookiesクラスの作成  
  20.     cookie = Cookies(self, max_age=COOKIE_EXPIRE_TIME)  
  21.   
  22.     return client, cookie  



3.ConsumerはUserをService Providerにリダイレクトさせる。この際Consumerは未認可のRequest TokenをURL Parameterに付加する

自サイトはUserをTwitterにリダイレクトさせます。Request TokenをGETパラメータに付与して。

上のソースを見ると、loginときたら、すぐにself.redirect(client.get_authorization_url())としていますが、

実は、client.get_authorization_url() の中でちゃんとTwitterにアクセスしてRequest Tokenを取得しています。
自サイトの「Twitterアカウントでログインする」を押すと、ちゃんと
http://twitter.com/oauth/authorize?oauth_token=
にアクセスしているのがブラウザ上で分かります。

  1. def get_authorization_url(self):  
  2.   """Get Authorization URL."""  
  3.   
  4.   token = self._get_auth_token()  
  5.   return "http://twitter.com/oauth/authorize?oauth_token=%s" % token  
  6.   
  7. def _get_auth_token(self):  
  8.   """Get Authorization Token. 
  9.  
  10.   Actually gets the authorization token and secret from the service. The 
  11.   token and secret are stored in our database, and the auth token is 
  12.   returned. 
  13.   """  
  14.   
  15.   response = self.make_request(self.request_url)  
  16.   result = self._extract_credentials(response)  
  17.   
  18.   auth_token = result["token"]  
  19.   auth_secret = result["secret"]  
  20.   
  21.   # Save the auth token and secret in our database.  
  22.   auth = AuthToken(service=self.service_name,  
  23.                    token=auth_token,  
  24.                    secret=auth_secret)  
  25.   auth.put()  
  26.   
  27.   # Add the secret to memcache as well.  
  28.   memcache.set(self._get_memcache_auth_key(auth_token), auth_secret,  
  29.                time=20*60)  
  30.   
  31.   return auth_token  


ちなみに、TwitterClientはこんなかんじ。
  1. class TwitterClient(OAuthClient):  
  2.   """Twitter Client. 
  3.  
  4.   A client for talking to the Twitter API using OAuth as the 
  5.   authentication model. 
  6.   """  
  7.   
  8.   def __init__(self, consumer_key, consumer_secret, callback_url):  
  9.     """Constructor."""  
  10.   
  11.     OAuthClient.__init__(self,  
  12.         "twitter",  
  13.         consumer_key,  
  14.         consumer_secret,  
  15.         "http://twitter.com/oauth/request_token",  
  16.         "http://twitter.com/oauth/access_token",  
  17.         callback_url)  


ちなみにちなみに、バックグラウンドでリクエスト送ってる箇所は↓のかんじ

  1. def make_async_request(self, url, token="", secret="", additional_params=None,  
  2.                    protected=False, method=urlfetch.GET):  
  3.     """Make Request. 
  4.  
  5.     Make an authenticated request to any OAuth protected resource. 
  6.  
  7.     If protected is equal to True, the Authorization: OAuth header will be set. 
  8.  
  9.     A urlfetch response object is returned. 
  10.     """        
  11.     payload = self.prepare_request(url, token, secret, additional_params,  
  12.                                    method)  
  13.     if method == urlfetch.GET:  
  14.         url = "%s?%s" % (url, payload)  
  15.         payload = None  
  16.     headers = {"Authorization""OAuth"if protected else {}  
  17.     rpc = urlfetch.create_rpc(deadline=10.0)  
  18.     urlfetch.make_fetch_call(rpc, url, method=method, headers=headers, payload=payload)  
  19.     return rpc  
  20.   
  21.   def make_request(self, url, token="", secret="", additional_params=None,  
  22.                                       protected=False, method=urlfetch.GET):  
  23.     return self.make_async_request(url, token, secret, additional_params, protected, method).get_result()  
  24.     



4.UserはService Provider上でConsumerへのアクセス権委譲を許可する。この際Service Providerは未認可のRequest Tokenを認可済とする

Userは、Twitterのページで、「許可する」といったボタンを押下し、アクセス権委譲を許可します。
すると、TwitterはRequest Tokenを認可済としてくれます。




5.Service ProviderはUserをConsumerにリダイレクトさせる。この際Service Providerは認可済のRequest TokenをURLに含める

TwitterはUserを自サイトにリダイレクトしてくれます。
このとき、GETパラメータに認可済みのRequest Tokenが付与されてます。


6.ConsumerはバックグラウンドでService Providerと通信を行い,認可済のRequest Tokenを実際のアクセス権を示すAccess Tokenと交換する

自サイトはバックグラウンドでTwitterと通信を行い、認可済のRequest TokenをAccess Tokenと交換してもらいます。

リダイレクトされたら、自サイトは以下の処理をします。

  1. #-----------------------------------------------------------  
  2. #OAuthの認証後に、アクセストークンをCookieに保存  
  3. #-----------------------------------------------------------  
  4. def VerityAuth(self, client, cookie):  
  5.     auth_token = self.request.get("oauth_token")  
  6.     auth_verifier = self.request.get("oauth_verifier")  
  7.     user_info = client.get_user_info(auth_token, auth_verifier=auth_verifier)  
  8.   
  9.     #アクセストークンをCookieへ保存  
  10.     cookie["user_token"] = user_info["token"]  
  11.     cookie["user_secret"] = user_info["secret"]  
  12.     cookie["screen_name"] = user_info["username"]  


client.get_user_info(auth_token, auth_verifier=auth_verifier)で、認可済のRequest TokenをAccess Tokenと交換してもらっています。

  1. def get_user_info(self, auth_token, auth_verifier=""):  
  2.   """Get User Info. 
  3.  
  4.   Exchanges the auth token for an access token and returns a dictionary 
  5.   of information about the authenticated user. 
  6.   """  
  7.   
  8.   auth_token = urlunquote(auth_token)  
  9.   auth_verifier = urlunquote(auth_verifier)  
  10.   
  11.   auth_secret = memcache.get(self._get_memcache_auth_key(auth_token))  
  12.   
  13.   if not auth_secret:  
  14.     result = AuthToken.gql(""" 
  15.       WHERE 
  16.         service = :1 AND 
  17.         token = :2 
  18.       LIMIT 
  19.         1 
  20.     """self.service_name, auth_token).get()  
  21.   
  22.     if not result:  
  23.       logging.error("The auth token %s was not found in our db" % auth_token)  
  24.       raise Exception, "Could not find Auth Token in database"  
  25.     else:  
  26.       auth_secret = result.secret  
  27.   
  28.   response = self.make_request(self.access_url,  
  29.                               token=auth_token,  
  30.                               secret=auth_secret,  
  31.                               additional_params={"oauth_verifier":  
  32.                                                   auth_verifier})  
  33.   
  34.   # Extract the access token/secret from the response.  
  35.   result = self._extract_credentials(response)  
  36.   
  37.   # Try to collect some information about this user from the service.  
  38.   user_info = self._lookup_user_info(result["token"], result["secret"])  
  39.   user_info.update(result)  
  40.   
  41.   return user_info  



7.Consumerは6)で得られたTokenを利用して,特定の情報にアクセスする


その他のメソッドなど。
  1. def _get_memcache_auth_key(self, auth_token):  
  2.   
  3.   return "oauth_%s_%s" % (self.service_name, auth_token)  
  4.   
  5. def _extract_credentials(self, result):  
  6.   """Extract Credentials. 
  7.  
  8.   Returns an dictionary containing the token and secret (if present). 
  9.   Throws an Exception otherwise. 
  10.   """  
  11.   
  12.   token = None  
  13.   secret = None  
  14.   parsed_results = parse_qs(result.content)  
  15.   
  16.   if "oauth_token" in parsed_results:  
  17.     token = parsed_results["oauth_token"][0]  
  18.   
  19.   if "oauth_token_secret" in parsed_results:  
  20.     secret = parsed_results["oauth_token_secret"][0]  
  21.   
  22.   if not (token and secret) or result.status_code != 200:  
  23.     logging.error("Could not extract token/secret: %s" % result.content)  
  24.     raise OAuthException("Problem talking to the service")  
  25.   
  26.   return {  
  27.     "service"self.service_name,  
  28.     "token": token,  
  29.     "secret": secret  
  30.   }  


Twitterからもらえるuser_infoは↓のかんじ
  1. def _lookup_user_info(self, access_token, access_secret):  
  2.   """Lookup User Info. 
  3.  
  4.   Lookup the user on Twitter. 
  5.   """  
  6.   
  7.   response = self.make_request(  
  8.       "http://twitter.com/account/verify_credentials.json",  
  9.       token=access_token, secret=access_secret, protected=True)  
  10.   
  11.   data = json.loads(response.content)  
  12.   
  13.   user_info = self._get_default_user_info()  
  14.   user_info["id"] = data["id"]  
  15.   user_info["username"] = data["screen_name"]  
  16.   user_info["name"] = data["name"]  
  17.   user_info["picture"] = data["profile_image_url"]  
  18.   
  19.   return user_info  

2010年10月17日日曜日

phpを始める

phpを始める。
目指せ!phpの資格!!

apacheのダウンロード
Windowsで開発します
apache windows binary [Download]

eclipseのダウンロード
開発環境はeclipse + pluginで。
pleiades eclipse 3.6 [Download]

参考書
やっぱオライリーでしょうか
プログラミングPHP 第2版

php
Windows用のphp
php5.3
php

2010年10月8日金曜日

jTemplatesを使ってみる

ajaxでの通信後、HTML表示が面倒だなーと思ってたら、↓を発見。
業界的には気づくの遅いか??

jTemplates

javascript内に書いたり、
HTMLコメントとして書いたり、
いろいろできるっぽいけど
テンプレートファイルに書き出すのがスマートっぽい。

テンプレートファイル


templateフォルダにsample.tplとか置いてみる。


  1. {#template MAIN}  
  2. <table id="dataTable" border="1">  
  3. <thead>  
  4.     <tr>  
  5.         <th>ID</th>  
  6.         <th>名前</th>  
  7.         <th>紹介</th>  
  8.     </tr>  
  9. </thead>  
  10. <tbody>  
  11. {#foreach $T as sample}  
  12.     <tr>  
  13.         <td>{$T.sample.id}</td>  
  14.         <td>{$T.sample.name}</td>  
  15.         <td>{$T.sample.article}</td>  
  16.     </tr>  
  17. {#/for}  
  18. </tbody>  
  19. </table>  
  20. {#/template MAIN}  



JavaScript


jqueryと一緒に読み込んで、$(document).readyで書いてみる。
ちなみにjqueryは1.4.2


  1. <script charset="utf-8" type="text/javascript" src="/javascripts/jquery-1.4.2.min.js"></script>  
  2. <script charset="utf-8" type="text/javascript" src="/javascripts/jquery-jtemplates.js"></script>  
  3. <script type="text/javascript">  
  4.         $(document).ready(function() {  
  5.   
  6.             var datas = new Array();  
  7.             var data1 = {id:1, name:"IS03",   article:"ワンセグ、お財布ケータイ機能を持ったauのandroid"};  
  8.             var data2 = {id:2, name:"iphone", article:"Apple社の大人気スマートフォン"};  
  9.             var data3 = {id:3, name:"Xperia", article:"sony ericsson(docomo)のandroid"};  
  10.             var data4 = {id:4, name:"garaxy", article:"Samsung社製(docomo)のandroid"};  
  11.   
  12.             datas.push(data1);  
  13.             datas.push(data2);  
  14.             datas.push(data3);  
  15.             datas.push(data4);  
  16.   
  17.                 $("#result").setTemplateURL("/templates/sample.tpl");  
  18.                 $("#result").processTemplate(datas);  
  19.   
  20.         });  
  21.     </script>  




なんとまあ便利なんだろうか!!