CakePHPのAuthコンポーネントを連携させてみた。
難しくて時間がかかってしまったので概念と実装方法を説明する。
概念
まずは概念から。
流れは以下の三つの図の通り。図1→図3という時系列で処理が進んでいく。
APIを使用する側のサイトをConsumerと呼ぶらしい。
また、パソコンの絵はユーザの端末を表している。
(excelで図を作ったが、bmpファイルにしたらぼやけて汚くなっちゃった。
どうすればきれいな図の画像ファイルできるかな。)
図1
図2
図3
実装方法
1.Twitter API https://dev.twitter.com のサイトで自分のサイトの情報を登録。
自分のTwitterアカウントでログインしたら右上にマウスを持っていくと現れる「My Applications」を押す。次の画面で「Create a new application」を押す。
現れる入力画面で自分のサイトの情報を入力する。
「Callback URL」以外は適当でもよさそう。
Callback URLは上の概念の「⑦Callback URLにリダイレクト」でTwitter側から
リダイレクトされる際のURLである。
このURLにはAuthコンポーネントで使用するモデルのコントローラに
専用のアクションを用意し、そのURLを指定する。
私の場合はUsersコントローラにtwtrCallback()というアクションを作ったので、
http://127.0.0.1/rakuzon/users/twtrCallback
とした。まだ、開発用なので自IPアドレスを指定している。
一つ注意点として、localhosotは指定できないので127.0.0.1を指定する。
このサイト参照。 Twitter アプリケーションのコールバックURLにlocalhostを指定する
2.Cnsumer KeyとConsumer Secretを控えておく
システムがTwitterのAPIを使うための、「システムに一つ」のIDとパスワードのようなもの。
「My applications」画面から、自分が今登録したサイト名をクリックした画面で、
Detailsタブを参照することにより確認できる。
これでTwitter側で行う作業は終わり。
3.OAuthを使うためのCakePHP用のライブラリをダウンロード、インストール
Consuming OAuth-enabled APIs with CakePHP
上記でダウンロードできる。
ただ、CakePHP 1.X系を使用している場合は、
「get the version for the older CakePHP 1.x.」
というリンクからダウンロードする。
自分はcakephp-cakephp-1.3.15-9-gacd25c3.zipを使っているが、
最初2.X用のものをダウンロードし使ってみたが、動かなかった。
ファイルを解凍したら、app/vendros直下にOAuthというフォルダをフォルダごと
配置する。これでインストールは完了。
4.ソースコードを書く
コメント増量中。
コメント中の丸の数字は上記の概念図の丸の数字に対応している。
まずはAuthで使うモデルのコントローラ。
私の場合は、users_controller.php
<?php //インストールしたライブラリを使用するためにこの1行を入れる。 App::import('Vendor','oauth',array('file'=>'OAuth'.DS.'oauth_consumer.php')); class UsersController extends AppController { var $name = 'Users'; var $components = array('Auth'); function beforeFilter() { $this->Auth->allow('index', 'login', 'twtrCallback'); //Authの認証で使うIDとパスワードはデフォルトでは'username'と'password'だが //それぞれaccess_token_keyとaccess_token_secreteに変更する。当然Usersテーブルにも同カラムを準備している。 $this->Auth->fields = array('username' => 'access_token_key', 'password' => 'access_token_secret'); //これをやらないと、app_controllerで行った設定が反映されないらしい。 parent::beforeFilter(); } //①で呼び出されるURL public function index() { $consumer=new OAuth_Consumer( '【2で控えたご自分のCnsumer Key】', '【2で控えたご自分のCnsumer Secret】'); // ②、③ request_tokenを取得。 //認証後、「http://127.0.0.1/rakuzon/users/twtrCallback」にリダイレクトする $requestToken=$consumer->getRequestToken( 'http://twitter.com/oauth/request_token', 'http://127.0.0.1/rakuzon/users/twtrCallback'); // 認証後、アクセストークンを取得する際に必要なので保存 $this->Session->write('request_token',$requestToken); // ④、⑤Twitterの認証ページにリダイレクト $this->redirect('http://twitter.com/oauth/authorize?oauth_token=' .$requestToken->key); } //⑦のリダイレクト先URLとなるアクション。 public function twtrCallback() { if (isset($this->params['url']['denied'])) { echo 'access denied'; return; } $consumer=new OAuth_Consumer( '【2で控えたご自分のCnsumer Key】', '【2で控えたご自分のCnsumer Secret】'); $requestToken=$this->Session->read('request_token'); //access_token取得する。 //⑧、⑨ access_tokenはユーザごとに払い出されるもの。 $accessToken=$consumer->getAccessToken( 'http://api.twitter.com/oauth/access_token',$requestToken); //⑩、⑪ access_tokenを使用し、ユーザ情報をTwitter側から引き出す。 $json = $consumer->get($accessToken->key, $accessToken->secret, 'http://twitter.com/account/verify_credentials.json', array()); $twitterData = json_decode($json, true); //⑫モデルに渡す。モデルでは保存が行われている。 $this->User->update( Array( "user_id" => $twitterData['id_str'], "user_name" => $twitterData['screen_name'], "access_token_key" => $accessToken->key, "access_token_secret" => $accessToken->secret, "img_url" => $twitterData['profile_image_url'] ) ); //⑬Authのlogin()処理のために型にaccess_token_keyとaccess_token_secretを入れてあげる。 $user['User']["access_token_key"] = $accessToken->key; $user['User']["access_token_secret"] = $accessToken->secret; //⑬Authのlogin処理 $this->Auth->login($user); $this->redirect('/main/index'); } public function logout(){ $this->Auth->logout(); } public function login(){ } }
users/indexがユーザがまずログインするときにアクセスするURLとなる。
つまり、ログインボタンなどに設定するURLだ。
twtrCallback()がTwitterからのリダイレクト先のURLとなる。
概念の「⑦CallbackURLにリダイレクト」。
63~70行目はログインするごとにユーザの情報をアップデートする
ことを意味している。
つづいて全てのコントローラーが継承するapp_controller.php。
class AppController extends Controller { function beforeFilter(){ //全ての画面でログイン状態のチェックを行うため、 //authのユーザ情報を取得する。 $auth=$this->Auth->user(); $this->set('auth',$auth); } }なお、view側で$authという変数に値が入っているかどうかで
ログインしているかどうかを確認する。全画面でログイン状態確認を
行うのでapp_controllerで行っている。
$this->session->check('Auth');で最初チェックしようと思ったのだが、
なぜかlogout後もtrueが帰ってきてしまう。
値の中身は空だったけど、変数の型が残っててもtrueとなってしまうからかも。
このあたりあまり定かではないけど、 ログイン状態確認の処理は、
後々Cookieを利用した自動ログインの機能を
追加する予定で、大きいプログラムになりそうなので、view側でなく、
controller側にあったほうがいいかな、と思い、深追いしないでおく。
そして、モデル。user.php
<?php class User extends AppModel { var $name = 'User'; public function update($user_data) { $user = $this->find('first', array('conditions' => array('user_id' => $user_data['user_id']))); if($user) { $user_data['id'] = $user['User']['id']; } $this->create(); $this->save(Array("User" => $user_data)); } }9~12行目でDB中にあるuser_idとTwitterから引っ張ってきた情報のuser_idを
引き合わせて、同じユーザ情報にはきちんと上書きしてくれるようになっている。
user_idというのはTwitterでTwitterアカウント一つ一つに割り当てられる数字のID。
このモデルに対応するテーブルは以下。
CREATE TABLE IF NOT EXISTS `users` ( `id` int(11) NOT NULL AUTO_INCREMENT, `user_id` int(64) NOT NULL, `user_name` varchar(64) NOT NULL, `access_token_key` text NOT NULL, `access_token_secret` text NOT NULL, `img_url` varchar(512) NOT NULL, `created` datetime NOT NULL, `updated` datetime NOT NULL, PRIMARY KEY (`id`), UNIQUE KEY `user_id` (`user_id`) ) ENGINE=MyISAM DEFAULT CHARSET=utf8 AUTO_INCREMENT=6 ;
users_controller.phpの80行目で、ログイン後にmain/indexに飛ばしているが、
飛ばし先でどのようにauthを扱っているかを解説するためmain_controller.phpを載せる。
<?php //3でインストールしたライブラリを使うための1行 App::import('Vendor','oauth',array('file'=>'OAuth'.DS.'oauth_consumer.php')); class MainController extends AppController { public $name = 'Main'; public $uses = array('Item','Tag','Theme'); //Authを使うため。 var $components = array('Auth'); var $paginate = array( 'Theme' => array ( 'limit' => 10, 'order' => array( 'theme.id' => 'asc' ) ), 'Tag' => array ( 'limit' => 10 ) ); function beforeFilter(){ //未ログイン状態でもindexにはアクセスを可能とするため。 $this->Auth->allow('index'); parent::beforeFilter(); } function index(){ //この辺はもともとあった処理 $this->Theme->hasMany['Item']['limit'] = 5; $this->Theme->hasMany['Item']['order'] = 'Item.id asc'; $this->Theme->unbindModel(array('belongsTo'=>array('User')),false); $themes = $this->paginate('Theme'); $tags = $this->paginate('Tag'); $this->set('themes',$themes); $this->set('tags',$tags); } }
11行目は全部のコントローラに設定するものなので本当はapp_controllerにもたせ
たかった。でもなぜかapp_controllerにもたせることができなかったので、各コントローラで
設定。ここ参照。[CakePHP]AppController でコンポーネント設定するとsessionコンポーネントが使えない。
28行目でindexを許容している。ログイン状態によって同じ画面でも出す部品を
変えようとしているだけだから、基本的に全部許容なんだよね。
だから、$this->Auth->allow(*);にしてアクセスさせない、登録系や管理系の画面だけ
$this->Auth->deny();で設定すればいい気もする。
つづいて、実際にTwitterのユーザ名やTwitterのプロフィール画像を表示するmain/index.ctp。
こいつは抜粋。
<?php if(!empty($auth['User']['id'])): ?> <div> <img src = "<?php echo $auth['User']['img_url'];?>"/> <br/> <?php echo $auth['User']['user_name'];?> </div> <?php endif;?>
1行目でcontroller側で取り出したauthのユーザ情報があるか確認。
無ければ、未ログインと判断し、3行目のプロフィール画像や5行目のユーザ名は表示しない。
この部品は全画面に共通的に使おうと思う。
あとは、必要に応じてlogin.ctpやlogout.ctp等のviewも作成する。
5.課題
この実装だと、ブラウザを閉じてもう一度アクセス、ログインしようとすると、
また、Twitterのアプリケーション認証画面に飛ばされて承認ボタンを押す必要
が出てくる。
それだと面倒くさいのでCookieを使用して自動ログインを実装してもいいかもしれない。
6.参考にしたURL
CakePHP で OAuth 認証を使ったログイン認証・保持や会員データの保持・更新をするコード
[プログラミング][PHP][CakePHP][twitter]CakePHPでOAuth
CakePHPでOAuthを使ったログインと自サービスのログインの両方に対応させてみました。
※CakePHPのバージョンはcakephp-cakephp-1.3.15-9-gacd25c3.zip
0 件のコメント:
コメントを投稿