うめぼしジョイスティック - ivoice

CakePHP、JavaScript、jQuery等のプログラミングについて書いていきます 思考は、うめぼしのように硬く、そして柔らかく。

CakePHP2.xで、複数ログインを可能にして、通常のログイン(Email、パスワード)とTwitter認証を使い分ける方法

※この記事では、oauthのコールバック処理はUsersコントローラのopauthComplete()アクションで行っています。

oauthの導入に関してはこちらを参照してください。

ivoice.hateblo.jp

さて、今回はCakePHP2.x系で 通常のログインPlugin ACLTwitter連携認証(oauth)を両方入れる方法です。 Plugin ACLは既に入っている環境を想定しています。そこにTwitter認証を入れることを考えます。

一番最初に、Usersテーブルに新しくTwitter用のカラムを追加しましょう。 twitter_id 、 token 、 token_secret の3つです。

twitter_idは bigint型にしましょう。以前のエントリーでも書きましたが、有効数字の桁数がint型では足りないからです。 tokenとtoken_secretはvarchar(255)あたりで大丈夫です。

で、まず、ログインフォームから送られるデータが重要になります。 通常のログインでは username と password がログインに必要なカラムとして指定されています。

しかし、ぼくの通常ログインではusernameの代わりにemailアドレスを使用したいので

Userモデルのlogin.ctp(ログインアクション)では

<?php

echo $this->Form->create('User', array('url' =>  array( 'controller' = > 'users', 'action' => 'login' )));
echo $this->Form->input('User.email', array('label'=>'email'));
echo $this->Form->input('User.password', array('label'=>'password'));
echo $this->Form->end('ログインする');

このようにしておきます。

そして、UsersController.php の beforeFilter()で、前処理をしてあげる必要があります。

1通り目 もし通常ログインの場合は ログインフィールドを 『username』を『email』 として設定する。

2通り目 もしTwitter連携ログインの場合は username を token(名前は任意) 、 password を token_secret(名前は任意) として設定する。

注意しなければならないのは、これらの『ログインするフィールドを複数設定する』処理は『フォームがポストされたあとに行う処理』として記述しなければうまく動かないということです。ログインの方法が1種類だった場合はそれが不要でした。

つまり何が言いたいかというと、 beforeFilter()の中の $this->request->is('post') の後にフィールド設定処理を書きましょう、ということです。

<?php

$this->request->is('post'){

//ここにログインフィールドを設定する処理を書く。ポストされたらログインフィールドの設定を行う。

}

より具体的にはこうです。

UsersController.phpのbeforeFilter()はこう記述する

<?php

public function beforeFilter() {
        parent::beforeFilter();//これがないと、親であるAppControllerのbeforeFilter()が実行されない

        $this->Auth->allow(array(
                                'mypage',
                                'login',
                                'logout',
                                'opauthComplete'
                                ));

        if($this->request->is('post')){
                if($this->request->data['User']['email']!=null){
                    $this->Auth->authenticate = array(
                    // フォーム認証を利用
                        'Form' => array(
                        // 認証に利用するモデルの変更
                            'userModel' => 'User', //Userモデルを指定
                            // 認証に利用するモデルのフィードを変更
                            'fields' => array('username' => 'email', 'password' => 'password')
                                    )
                    );

                }else if(isset($this->data['auth']['credentials']['token'])){

                    $this->request->data['User']['username'] = null;
                    $this->request->data['User']['password'] = null;


                    $this->Auth->authenticate = array(
                    // フォーム認証を利用
                        'Form' => array(
                        // 認証に利用するモデルの変更
                        'userModel' => 'User', //Userモデルを指定
                        // 認証に利用するモデルのフィードを変更
                        'fields' => array('username' => 'token', 'password' => 'token_secret')
                        )
                    );
                }
        }
    }

このようになります。通常フォームでのログインとTwitter認証でのログインを分けている(if文)のが分かるでしょう。

前処理がやや複雑になりますが、これでログインの種類ごとに、ログインで使うフィールドを選ぶことができます。 つまり、複数ログインを可能にしつつも Usersテーブル1つで済むという利点が生まれるのです。

isset($this->data['auth']['credentials']['token'])の部分が重要で、ここで何をやっているかというと、Twitter認証でコールバックされた場合は$this->dataの中にそのユーザーのTwitter情報が入るので、そこの値が取れるかどうか(コールバックしているかどうか)を調べているのです。

そして、もしトークンが検出された場合(Twitterでコールバックしているとき)には、$this->request->data(フォームの値)を操作してあげて、 username、passwordを nullにしてあげる必要があります。 これは、誤動作をおこさないようにするためです。何か値が入っていると通常ログインとして処理されてしまうからです。 もしTwitterからのログインの場合は、usernameとpasswordをnullに設定しましょう。

どうやら、 $this->Auth->login(); のログイン処理では、$this->request->dataの中に入っている2つの値をもとに、Userテーブルを探索しているようなのです。 その2つのフィールドは、通常では(usernameとpassword)です。そして、ここではTwitter認証にしたいので、フィールドに token と token_secret を指定している、というわけです。

ちょっとややこしいですが、ここの部分さえ分かれば、大丈夫でしょう。

そして、そのあとに、実際のコールバック処理(Twitterで認証されて飛んでくるアクション)の部分でログイン処理を行います。

<?php

public function opauthComplete() {


            $this->request->data['User']['token'] = $user_twi['auth']['credentials']['token'];
            $this->request->data['User']['token_secret'] = $user_twi['auth']['credentials']['secret'];

            if ($this->Auth->login()) {

                $this->redirect('/users/mypage');

            } else {
                $this->Session->setFlash('ログアウトしています。アカウントを作成しますか?');
            }


}


このような形で 擬似的にそのフィールドを $this->request->dataの中に作って、コールバックでTwitterから送られてきた値も入れてあげます。

$this->request->data['User']['token'] (トークン) $this->request->data['User']['token_secret'] (トークンパスワード)

この部分です。 これらがUserテーブル内にあれば、そのユーザーとしてログインできる訳です。

言い方を変えると、ログイン処理では、Userテーブルを参照して、 トークンとトークンパスワードが合致するユーザを見つけて、もしいる場合はログインが完了するようです。

また、opauthComplete()アクション(命名は自由です。aouthでTwitterに入ったときのコールバック処理のこと)では、 ユーザー登録されていないトークンとトークンパスワード、つまり新規のTwitterアカウントの場合は、最初に登録処理を行う必要があります。

しかし、トークンパスワードは、このままだと、保存される際にハッシュ化(暗号化)されません。 なので、モデルに記述を追加する必要があります。 以下のようにしましょう。

User.php

<?php

    public function beforeSave($options = array()) {
        if(isset($this->data['User']['password'])){
            $this->data['User']['password'] = AuthComponent::password($this->data['User']['password']);
        }
        if(isset($this->data['User']['token_secret'])){
            $this->data['User']['token_secret'] = AuthComponent::password($this->data['User']['token_secret']);
        }

        return true;
    }


このように記述をして、token_secret(トークンのパスワード)が保存される場合にAuthComponent::passwordでハッシュ化(暗号化) してあげる必要があります。

以上が、ログイン処理を分ける方法の手順になります。

ポイントは、

①ログイン認証ではフォームからのデータ $this->request->data の2つのデータを見ているということ。$this->Auth->login()を行う前に。$this->data->requestに2つの値を入れてあげる。1つは【ユーザー名】でもう一つは【パスワード】の役割。

②コントローラのbeforeFilterで、 通常ログインの場合とTwitterログインとの場合で、ログインに使用するフィールドをそれぞれ指定する。 ($this->Auth->authenticateの処理をif文で複数書く)

③モデルでは、トークンのパスワードを保存するときのハッシュ化の処理をbeforeSaveで忘れずにしておくこと。

この3つです。特に①の部分は重要で、フォームから送られるデータをCakePHPは見ているようなので、必ずフォームから送られてくるデータを サンプルコードのような分岐(if文)で書き換えてあげる必要があるということを覚えておきましょう。

以上です。