Twitterのログイン認証を自分のサイトで利用できるAPIを使用し、
CakePHPのAuthコンポーネントを連携させてみた。
難しくて時間がかかってしまったので概念と実装方法を説明する。
概念
まずは概念から。
流れは以下の三つの図の通り。図1→図3という時系列で処理が進んでいく。
APIを使用する側のサイトをConsumerと呼ぶらしい。
また、パソコンの絵はユーザの端末を表している。
(excelで図を作ったが、bmpファイルにしたらぼやけて汚くなっちゃった。
どうすればきれいな図の画像ファイルできるかな。)
図1
図2
図3
実装方法
自分の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