更新日:$Date:: 2013-08-06 21:44:57 +0900#$
PerlのCGIでNet::Twitterモジュールを利用し、Twitterアカウントによるログイン実装、Webアプリケーションを作るための入門手引き。初心者向け(たぶん)。
OAuthのことがいつも分からなくなるので、半分は自分用メモです。はてなダイアリーに書いたボット作成メモから、つらつら加筆修正しました(PerlでTwitterアプリ開発の導入メモ - Net::Twitter)。
さくらのレンタルサーバにて、以下の環境で作ってみた。
GitHubからどうぞ GitHub: Twitter-Webapp-Sample
はじめに、全体の流れの概要について述べておく。OAuthについての知識。
色々と登場人物が多くてややこしいけど……最終的には、アプリのConsumer keyとConsumer secret、そしてアカウントのAccess TokenとAccess Token Secret、の4つだけあれば良い。途中のリクエストトークンとかは一時的なもの。
どうしてこんなメンドいことになっているかもちょっと述べておく。まず、アプリに直接Twitterのアカウントとパスワードを入れさせるのは、アプリの制作者に悪意があった場合、パスワードがそのまま悪用できるので良くない(というかあり得ない実装)。一方このOAuth形式ならば、ユーザがTwitterパスワードを入力するのは、あくまで認証用URL(これはTwitter側のページ)なので、アプリ制作者にパスワードが渡ることは無い。
また、Webアプリの制作者に悪意があった場合、ログイン後はあなたのAccess TokenとAccess Token Secretは既にアプリ制作者の手中にあるため、何かしらの操作をTwitter上で勝手におこなうことができる。しかし、ユーザは悪さをされた時点でTwitterの設定ページにログインし、該当アプリへの許可を"禁止"すれば(内部的には特定のConsumer Keyによる操作が禁止される)、以後は盗んだ人が該当アプリ経由で何かすることはできなくなる。という感じ。
まずはじめに、作りたいWebアプリケーションをTwitterにログインして登録する。Twitter Developersにログインして、右上のアカウントのところから My applications → Create a new application とする。
必須項目は"Name", "Description", "Website"の3つなので、実質何も無くても登録はできる。でもまぁ、それなりのWebとアイコンを用意してからやった方が気分も盛り上がって良いかも。Callback URLにはプライベートIPアドレスで書くことができるので、最初は http://192.168.1.10/Sample/callback.cgi みたいに、ローカル環境でテストすることもできる。
アプリの登録ができたら、登録画面にConsumer keyとConsumer secretが発行されているので、これをメモっておく。この値は以降の手順で使う。
OAuthでTwitterにログインする動きは非常に分かりづらいため、図を作ってみた。
この図で示しているログインはlogin.cgi、コールバックはcallback.cgiとして設置するものとする。
まず、図の(1)でユーザからログインを受け付けるlogin.cgiの中身はこうなっている。図の番号で言うと、(3)までをこの中で行っていることに注意。
#!/usr/bin/perl use strict; use warnings; use Net::Twitter; use CGI; my $cgi = new CGI; my $nt = Net::Twitter->new( traits => [qw/API::RESTv1_1/], consumer_key => "xxxxxxxxxxxxxxxxxx", consumer_secret => "ZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZ", ); my $url = $nt->get_authorization_url( callback => 'http://ozuma.sakura.ne.jp/Twitter-Webapp-Sample/callback.cgi' ); print 'Set-Cookie: ' . $cgi->cookie( -name => 'token', -value => $nt->request_token, -path => '/Twitter-Webapp-Sample' ) . "\n"; print 'Set-Cookie: ' . $cgi->cookie( -name => 'token_secret', -value => $nt->request_token_secret, -path => '/Twitter-Webapp-Sample' ) . "\n"; print $cgi->redirect(-uri => $url);
このソースではNet::Twitterオブジェクトを作成した後に、コールバック値(戻り先のURL)を指定して、認証URLを組み立てるためのget_authorization_urlメソッドを実行している。これにより、アプリはまず裏側でTwitter APIに対して/oauth/request_tokenを叩き、Request TokenおよびRequest Token Secretを取得する。この二つは、後述のAccess TokenおよびAccess Token Secretを取得するための、いわば一時的なトークンである(よって、使い捨てである)。
取得できたRequest TokenとそのSecretは、Net::Twitterオブジェクトのrequest_tokenメソッドおよびrequest_token_secretメソッドで取得できるので、それをユーザのブラウザのCookieにSetしている(このRequest Tokenは、あとでコールバック後に再度使うため)。そこまで出来たら、最終行でブラウザにリダイレクトを発行し、組み立てたTwitterの認証URLに飛ばしている。
続いて図の(4)-(6)を解説する。先ほどのloginc.cgiの最終行で飛ばされたTwitterの認証ページは、こんな感じになっている。WebベースのTwitterアプリを使ったことがあるなら、見たことある画面だろう。
URLの引数を見ると、oauth_tokenというパラメタを付けている。これが先ほど取得したRequest Tokenであり、Twitter側はこれにより先ほどAPIで返したRequest Tokenと該当Twitterユーザを対応させることができる。この画面でのログインに成功すると、Twitter側はユーザを指定されたコールバック先に誘導すると共に、verifierという値を付けて返す。以下のようなURLにリダイレクトすると思えば良い。
http://ozuma.sakura.ne.jp/Twitter-Webapp-Sample/callback.cgi?oauth_token=xxxxxx&oauth_verifier=ZZZZZZZZ
コールバックを受け取るcallback.cgiは、Twitter側から払い出されたverifierをユーザ経由で取得し、再度TwitterのAPIにアクセスする。この際には、
を1セットにして、いよいよ本物のトークン、Access Tokenをリクエストする。(Consumer KeyとそのSecretはアプリごとの設定だからアプリ自身が知っている。Request TokenとSecretは、先ほどloginc.cgiの最後に設定したユーザのブラウザのCookieに入っているからこれを取り出す。verifierはcallbackのGET引数にくっついている。)
さて、このAccess Tokenを取得するのが、Net::Twitterオブジェクトのrequest_access_tokenメソッドである。callback.cgiの全体のソースを以下に示す。
#!/usr/bin/perl use strict; use warnings; use Net::Twitter; use CGI; use CGI::Session; my $cgi = new CGI; my $session = new CGI::Session(undef, undef, {Directory=>'/tmp'}); $session->expire('+12h'); my $oauth_verifier = $cgi->param('oauth_verifier'); my $nt = Net::Twitter->new( traits => [qw/API::RESTv1_1/], consumer_key => "xxxxxxxxxxxxxxxxxx", consumer_secret => "ZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZ", ); $nt->request_token($cgi->cookie('token')); $nt->request_token_secret($cgi->cookie('token_secret')); my($access_token, $access_token_secret, $user_id, $screen_name) = $nt->request_access_token(verifier => $oauth_verifier); $session->param(-name=>'access_token', -value=>$access_token); $session->param(-name=>'access_token_secret', -value=>$access_token_secret); $session->param(-name=>'user_id', -value=>$user_id); $session->param(-name=>'screen_name', -value=>$screen_name); my $cookie = CGI::Cookie->new(-name => 'CGISESSID', -value => $session->id(), -expires => '+12h', -path => '/Twitter-Webapp-Sample' ); print $cgi->header( -cookie=>$cookie, -location => 'http://ozuma.sakura.ne.jp/Twitter-Webapp-Sample/' );
この実装では、request_access_tokenで取得したAccess Tokenなどを、セッションを発行してそこに格納している。そしてクッキーにセッションIDを設定している。これによりこのブラウザで再びアクセスがあった場合のユーザ管理を可能としている。
最後に、callback.cgiはlocationヘッダでリダイレクトしている。ここではログイン完了画面、あるいはサービスのTOPページに返すのが普通だろう。
前章までの操作でAccess TokenおよびAccess Secretが取得できたので、これを用いてTwitter操作してみることを考える。ここでは簡単な、「ログインしているユーザへの最新のmention (@userid で書かれた自分宛のツイート)を表示する」というのをやってみよう。
ログインをはしょって書くと、今回はこんな図になる。アクセストークンを取得できれば、あとはアプリがTwitter APIで好き勝手にできるというイメージ。
ここでの実装では、(3)Twitter操作依頼、はindex.cgiで行うこととする。
前章で示した通り、callback.cgiはセッション内にAccess TokenとSecretを記録したので、これを用いてNet::Twitterオブジェクトを生成すれば良い。このサービスのTOPページと見なしているindex.cgi内で、以下のようにオブジェクトを生成する。
my $nt = Net::Twitter->new( traits => [qw/API::RESTv1_1/], consumer_key => "xxxxxxxxxxxx", consumer_secret => "ZZZZZZZZZZZZZZZZZZZZZZZZZZZZZ", access_token => $session->param(-name=>'access_token'), access_token_secret => $session->param(-name=>'access_token_secret'), );
mentionとは、@userid で特定ユーザ宛に発信されたツイートのこと。このうち自分宛のmentionを取得するには、Net::Twitterオブジェクトのmentionsメソッドを使えば良い。詳しくは perldoc Net::Twitter してみて欲しいけど、とりあえず引数は空っぽでも構わない(その場合は最新20件が返る)。
mentionsメソッドは、具体的にはTwitter APIのstatuses/mentions_timelineの中身を返してくれる。APIではここはJSONで返されるが、Net::TwitterモジュールでPerlのデータ形式にして返してくれるので、自分でJSONをパースする必要は無い。それぞれのmentionをハッシュリファレンスにしたものの配列が、配列リファレンスで返されるので、使うときは配列にデリファレンスして一つ一つ取り出して処理……という流れが多い。
サンプル例: my $res = $nt->mentions(); foreach my $mention (@$res) { my $screen_name = $mention->{user}->{screen_name}; ... ... }
実際にmentionオブジェクトから取り出せる項目と値については、先ほどのAPI仕様書にだいたい書いてあるけど、よく使うものをとりあえず抜き出して表にしておく。
項目 | コーディング例 | 値の例 |
---|---|---|
mentionの内容 | $res->{text} | @ozuma5119 こんにちはー |
mentionの投稿日時 | $res->{created_at} | Sat Dec 29 03:17:41 +0000 2012 |
mentionのID | $res->{id} | 284860714978525184 |
mentionしてきたユーザアカウント | $res->{user}->{screen_name} | ozumaBot |
mentionしてきたユーザ名 | $res->{user}->{name} | 有川雄妻 |
mentionしてきたユーザのフォロワー数 | $res->{user}->{followers_count} | 2 |
というわけで、作ってみたサンプルのTOPページ(ただのテキストだけど……)はこんな感じになる。
#!/usr/bin/perl use strict; use warnings; use CGI; use CGI::Session; use Net::Twitter; my $NOT_LOGIN_MES = "You are not logged in. Access: \n\n" . "http://ozuma.sakura.ne.jp/Twitter-Webapp-Sample/login.cgi\n"; my $cgi = new CGI; print $cgi->header(-type=>'text/plain', -charset=>'utf-8'); unless ($cgi->cookie("CGISESSID")) { print $NOT_LOGIN_MES; exit; } my $session = new CGI::Session(undef, $cgi->cookie("CGISESSID"), {Directory=>'/tmp'}); unless ($session->param(-name=>'access_token')) { $session->close; $session->delete; print $NOT_LOGIN_MES; exit; } my $nt = Net::Twitter->new( traits => [qw/API::RESTv1_1/], consumer_key => "xxxxxxxxxxxx", consumer_secret => "ZZZZZZZZZZZZZZZZZZZZZZZZZZZZZ", access_token => $session->param(-name=>'access_token'), access_token_secret => $session->param(-name=>'access_token_secret'), ); my $res = $nt->mentions({count => 1}); my $mention_status = $res->[0]->{text}; my $mention_from = $res->[0]->{user}->{screen_name}; print "Now You are logged in as: \@" . $session->param(-name=>'screen_name') . "\n"; print "Latest mention from \@$mention_from: $mention_status\n";
まずはじめに、login.cgiの後のcallback.cgiで発行しているはずの、CGISESSIDというCookieの存在をチェックして無ければログインを促して終了とする。
続いてCookieにCGISESSIDがあれば、これをIDにセッションをnewする。この際、Cookieにだけ値が残っていてサーバ側セッションが無いかもしれないので、必須のaccess_tokenというパラメタ値がセッション内に入っているかチェックしている。
セッションがOKならば、以前のログイン情報からAccess TokenとSecretを引っ張り出し、これでNet::Twitterオブジェクトを生成する。続いてこのオブジェクトにmentionsメソッドを利用して、該当ユーザのmentionを取得する。ここでは1件のみ取ることとしたので、countオプションを利用した。
次に、mentionsメソッドの戻り値$resから、ツイート内容とツイートしてきたユーザ名を取得する。最後にこれを表示してオシマイ。