Catalyst::AuthenticationでTwitter認証を使う方法
CatalystでTwitter認証をやろうとしたら結構ハマったのでメモを作っておく。
Catalyst::Authentication::Credential::Twitter(以下CAC:Twitter)ってのがあるんだけど、使い方が難しかった。
認証の流れは、
- アプリからTwitterの認証URLにアクセス
- Twitterでアクセスを「許可する」ボタンを押す
- アプリのcallback URLで認証処理
- アプリのDBにユーザー情報がなければ、ユーザー情報を登録。
- あとはよしなに。
アプリ名はMyApp、DB名はMyDBとした。
環境は、さくらレンタルサーバの一般プラン(VPSじゃない)で、local::libを使ってCPAN環境を作っている。詳しくはこちらを参考に。
http://blog.hide-k.net/archives/2009/02/locallibrootcpa.php
1.Twitterにアプリケーションを登録
PHPでTwitter APIのOAuthを使う方法まとめ - 頭ん中
この記事の「Twitter にアプリケーションを登録する」を参考に登録した。
アプリケーションの種類はブラウザアプリケーション。
コールバックURLはアプリのコンフィグで上書きできるので適当でよさそう。
Twitterでログインするは、有効にしておく。
登録したら、Cunsumer Key と Consumer secret が表示されるので、コピペしておく。
2.MyApp.pmの編集
package MyApp; use Moose; use namespace::autoclean; use Catalyst::Runtime 5.80; # (1)プラグインを追加 use Catalyst qw/ -Debug ConfigLoader Static::Simple Unicode Authentication Session Session::PerUser Session::Store::File Session::State::Cookie /; extends 'Catalyst'; our $VERSION = '0.01'; $VERSION = eval $VERSION; __PACKAGE__->config( name => 'MyApp', disable_component_resolution_regex_fallback => 1, ENCODING => 'utf-8', TEMPLATE_EXTENSION => '.tt', "Plugin::Authentication" => { default_realm => "twitter", realms => { twitter => { credential => { class => "Twitter", # (2) CAC::Twitterを指定 }, store => { class => 'DBIx::Class', # (3) StoreはDBIx::Classを使用 user_model => 'MyDB::twitteruser', # (4) ユーザー情報を格納するテーブル名を指定 }, auto_create_user => 1, # (5) 自動ユーザー登録をON consumer_key => 'xxxxxxxxxxxxxxxxxxxxx', # (6) Twitterにアプリケーションを登録した時に取得した値を入れる。 consumer_secret => 'xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx', # (6) callback_url => 'http://<アプリのベース>/callback', # (7) }, }, }, );
プラグイン(1)は、Authentication以下が追加したもの。Session::PerUserはCA::Credential::Twitterが使う。ソース(Twitter.pm)に以下の記述があるけど、
use Catalyst qw/ Session::PerUser /;
僕の環境では効いてないっぽい。
Session管理はお好みで。今回はStore::FileとState::Cookieを使うことにした。
configでは、AuthenticationのcredentialクラスにTwitterを指定(2)。CAC::Twitterは、StoreにDBIx::Classを使うみたいなので、それを指定(3)。ユーザー情報を格納するテーブル名はtwitteruserとした(4)。
CAC::Twitterでは、認証処理時、テーブルにデータがないと認証エラーになるので、初回ログイン時にテーブルにデータを作ってやる必要がある。そのオプションを有効にする(5)。
consumer_keyとconsumer_secretは、1.Twitterにアプリケーションを登録の手順で取得した値を入れる(6)。
callback_urlには、認証処理をするアクションを指定する(7)。
3.Root.pmの編集
ログインURLへアクセスするアクションと、認証処理をするアクションを書く。ほぼCAC::Twitterにあるサンプル通り。
sub login : Local { my ($self, $c) = @_; my $realm = $c->get_auth_realm('twitter'); $c->res->redirect( $realm->credential->authenticate_twitter_url($c) ); # (1) }
Twitterの認証URLにリダイレクト(1)。
sub callback : Local { my ($self, $c) = @_; if (my $user = $c->authenticate(undef,'twitter')) { # (1) 認証処理 $c->res->redirect("/"); # (2) 認証成功したら / にリダイレクト } else { die 'login error.'; # (3) 認証失敗したらdie。 } }
認証処理を実施して(1)、成功していたら / にリダイレクトする(2)。失敗したらdie(3)。auto_create_userを有効にしているので、callbackに来てから認証に失敗するのは例外でいい。
ユーザー情報テーブルの作成
ユーザー情報を格納するテーブルを作成する。テーブル名は、MyApp.pmで指定した"twitteruser"。
CREATE TABLE IF NOT EXISTS `twitteruser` ( `id_field` int(11) NOT NULL AUTO_INCREMENT, `twitter_user` varchar(100) DEFAULT NULL, `twitter_user_id` int(11) NOT NULL, `twitter_access_token` varchar(100) DEFAULT NULL, `twitter_access_token_secret` varchar(100) DEFAULT NULL, PRIMARY KEY (`id_field`) ) ENGINE=MyISAM DEFAULT CHARSET=utf8 AUTO_INCREMENT=0 ;
5項目すべてが必要。ひとつでも無いと認証でエラーになる。
id_fieldの名前は変更可能で、別の名前を使いたければ、MyApp.pmのコンフィグで指定してやればいい。他の項目名は変更できない。
id_fieldがこのテーブルのIDになる。自動付番(AUTO_INCREMENT)を有効にしておく。
項目名 | 入るデータ | 例 |
---|---|---|
twitter_user | twitterユーザー名 | hsksyusk |
twitter_user_id | twitterユーザーID | 3705551 |
twitter_access_token | 3705551-xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx | |
twitter_access_token_secret | <文字列> | xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx |
twitterユーザー名は変更可能なので、IDでユーザーを判別するようにする。
ためしに自分で登録したところ、twitter_access_tokenは50桁、twitter_access_token_secretは43桁だった。固定かどうかわからないので100桁とっておく。twitter_user_idはintで間に合うか不安。
モデルクラスの作成
テーブルを作ったら、そこにアクセスするモデルクラスを作る。モデルクラスは、Catalyst::Model::DBIC::Schemaを使って、動的に生成する。
この記事を参考に。
初めてのCatalyst入門(7) モデルを使ったプログラミング (1/5):CodeZine(コードジン)
モデルを作成するときのコマンドは、僕の環境だとこう。
$ ./MyApp/script/myapp_create.pl model MyDB DBIC::Schema MyApp::Schema create=static dbi:mysql:database=MyDB:host=mysql231.db.sakura.ne.jp <username> <password>
auto_createメソッドの追加
configでauto_create_userを有効にしているので、認証時にユーザー情報が無いとき、MyApp::Schema::ResultSet::Twitteruserのauto_createメソッドが呼ばれる。このメソッドは自分で用意する必要がある。
MyApp/lib/MyApp/SchemaにResultSetディレクトリを掘って、そこにTwitteruser.pmを作る。
MyApp/lib/MyApp/Schema/ResultSet/Twitteruser.pm
package MyApp::Schema::ResultSet::Twitteruser; use strict; use warnings; use base qw/DBIx::Class::ResultSet/; sub auto_create { # (1) auto_createメソッドを作る。 my ( $class, $hashref, $c ) = @_; my $member = $class->create({ twitter_user_id => $hashref->{twitter_user_id}, # (2) twitterユーザーIDを登録する。 }); return $member; } 1;
Twitteruser.pmの中にauto_createメソッドを作る(1)。twitterユーザーIDを持たせて、twitteruserテーブルに1レコード追加する。id_fieldは自動付番される。残りの項目はレコード作成時はNULLで、認証処理の中でUPDATEされる。
MyApp/lib/MyApp/Schema/Result/Twitteruser.pmの最後のほうに、1行追加する。
MyApp/lib/MyApp/Schema/Result/Twitteruser.pm
# Created by DBIx::Class::Schema::Loader v0.07002 @ 2011-01-21 19:00:34 # DO NOT MODIFY THIS OR ANYTHING ABOVE! md5sum:MSuvL29yw4VVYWN2sNL20w __PACKAGE__->resultset_class('MyApp::Schema::ResultSet::Twitteruser'); # (1) 追加行 # You can replace this text with custom content, and it will be preserved on reg
(1)の行を追加することで、ResultSet::Twitteruserにアクセスできるようになる。
メソッドの追加のしかたは、こちらの記事を参考にした。
第38回 DBIx::Class:拡張性の高さが売りではありますが:モダンPerlの世界へようこそ|gihyo.jp … 技術評論社
テスト
テスト用にRoot.pmにアクションを追加する。
sub index :Path :Args(0) { my ( $self, $c ) = @_; if ( $c->user_exists) { # (1) ログイン状態を判断 $c->response->body( $c->user->get('twitter_user').' is logged in.' ); # (2) ログインユーザーのTwitterユーザー名を取得 } else { $c->response->body( 'not login.' ); } } sub logout :Local { # (3) ログアウト処理を実装 my ( $self, $c ) = @_; $c->logout(); $c->response->redirect($c->uri_for('/')); }
callbackで認証後、/ に飛ばされるので、そこでログイン状態を判断して、表示メッセージを切り替える処理を入れる(1)。ログインしていれば、ユーザー名を取ってきて表示する(2)。ログアウト処理も作っておく(3)。
以下、テスト手順。
- http://<アプリのベース>/login にアクセス。
- https://api.twitter.com/oauth/authorize?oauth_token=xxxxxxxにリダイレクトされる。Twitterに登録したアプリケーション名が表示される。「許可する」ボタンを押す。
- http://<アプリのベース>/にリダイレクトされる。"<ユーザー名> is logged in."と表示される。
- リロードなどしても、"<ユーザー名> is logged in."と表示される。
- DBのtwitteruserテーブルを確認すると、ログインしたtwitterユーザーの情報が登録されている。
- http://<アプリのベース>/logout にアクセス。
- http://<アプリのベース>/にリダイレクトされる。"not login."と表示される。
- リロードなどしても、"not login."と表示される。
ここまで動けばOKです。あとは各ページでユーザーIDやらユーザー名やらを取得して、よろしくやってください。
参考URL
これまでに紹介した以外にも、参考にした記事をご紹介。
[僕] CA::Creditional::* で auto_create
初めてのCatalyst入門(12) ログイン/セッション管理 (1/5):CodeZine(コードジン)
Catalyst::Authentication::Credential::Twitter - Twitter authentication for Catalyst - metacpan.org
http://blog.hide-k.net/archives/2009/02/locallibrootcpa.php
PHPでTwitter APIのOAuthを使う方法まとめ - 頭ん中
初めてのCatalyst入門(7) モデルを使ったプログラミング (1/5):CodeZine(コードジン)
第38回 DBIx::Class:拡張性の高さが売りではありますが:モダンPerlの世界へようこそ|gihyo.jp … 技術評論社