[CakePhp]ログイン処理を外部APIを使用して実装する

DBからの情報取得ではなく、外部のAPIにアクセスして認証したかったので
Authコンポーネントをカスタマイズしてみた。

画面遷移としては、ユーザー名(メールアドレス)とパスワードを入力し、
APIに接続してOKならログインする、というもの。

Authenticate

おーせんてぃけーと。
上記で書いた通り、フォームから送信されたユーザー名とパスワードを使用してログインするので、
FormAuthenticateを継承する。
オーバーライドする関数は、authenticate_findUser

/app/Controller/Component/Auth/UseAPIAuthenticate.php

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
<?php
App::uses('FormAuthenticate', 'Controller/Component/Auth');

class UseAPIAuthenticate extends FormAuthenticate {

    public function authenticate(CakeRequest $request, CakeResponse $response)
    {
      //コンポーネントの設定を読み込む
        $userModel = $this->settings['userModel'];
        list(, $model) = pluginSplit($userModel);
        $fields = $this->settings['fields'];
        if (!$this->_checkFields($request, $model, $fields)) {
            return false;
        }

        return $this->_findUser(
            $request->data[$model][$fields['username']],
            $request->data[$model][$fields['password']]
        );
    }

    public function _findUser($username, $password = null)
    {
        // ログイン実施
        $this->user = null;
        $ret = ClassRegistry::init('Api')->login($username, $password);
        if (! $ret) {
            //なんらかのログインエラー
            return false;
        }

      //ユーザーデータ。中身はニーズに合わせて
        $result = Array(
            "username" => $username,
            "result" => $ret
        );
        return $result;
    }

}

APIに接続する処理は、Apiというモデルに記述しているとする。
コンポーネントからモデルを使用するのはあまりよくないともどこかで見たが、標準のAuthコンポーネントも使用していたのでよしとする。
その際の使用方法は以下のようになる。

ClassRegistry::init('Api')

ログイン成功後、返却するデータの中身は、配列である必要があるよう。
trueとだけ返してても認証されなかった。

AppController

AppControllerにて、上記のAuthコンポーネントの設定を書く。

/app/Controller/AppController.php

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
<?php
App::uses('Controller', 'Controller');

class AppController extends Controller {

    public $uses = array('User', 'Api');

    public $components = array(
        'Session',
        'Auth' => array(
            'authenticate' => array(
                'UseAPI' => array(
                    'userModel' => 'User',
                    'fields' => array(
                        'username' => 'email'
                    )
                )
            )
        )
    );
        
    ....略....
}

作成したクラスファイル名が、UseAPIAuthenticateの場合は、UseAPIと、Authenticateを引いて指定する。
これは実際にCakeのAuthコンポーネント内で、以下のように指定されているため。

/lib/Cake/Controller/Component/AuthComponent.php 795行目

$className = $class . 'Authenticate';

で、使用するモデルは、Userとし、今回はメールアドレスでのログインということで、usernameemailを使用する指定をしている。

UsersController

で、ログインページで実行される処理をUsersControllerに書く。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
<?php
App::uses('AppController', 'Controller');
class UsersController extends AppController
{
  public $uses = array('User', 'Api');
  
  public function beforeFilter()
    {
        $this->Auth->allow('login', ...認証除外ページ);
        parent::beforeFilter();
    }
    
    /**
     * ログイン
     */
    public function login()
    {
        //POST値がある?
        if (! $this->request->is('post')) {
            // ログインフォームを表示
            $this->logout(); //ログアウトしてログインページを表示させる処理
            return;
        }

        /// authコンポーネント使用
        //ログイン
        $result = $this->Auth->login(); //trueかfalseが返ってくる
        $data = $this->Auth->user(); //UseAPIAuthenticateで設定したユーザーデータを取得出来る
        
        if (!$result) {
            $this->Session->setFlash(__('ユーザ名もしくはパスワードが正しくありません'));
            return;
        }

        //トップページへ
        $this->redirect(array('action' => 'index'));
    }
    
    ....略....
}

$this->Auth->login()とするだけでフォームで入力された内容が引き継がれてログイン処理が行われる。
実際の処理は、/lib/Cake/Controller/Component/AuthComponent.phpで行われている。
ログインの結果は、trueかfalesで返ってくるのでfalseの場合はエラーを表示している。
ログイン成功時にセットしたユーザーデータは、$this->Auth->user()で取得できる。

Model

実際にAPIを使用してログインしている箇所は側だけ。中身は割愛。

/app/Model/Api.php

1
2
3
4
5
6
7
8
9
10
11
<?php
App::uses('AppModel', 'Model');
class Sf extends AppModel
{
  public function login($email, $pw)
    {
        //何かしらAPIへの接続処理

        return $result;
    }
}

View

最後はビュー。フォーム部分だけ。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
<?php echo $this->Form->create('User', array('novalidate' => true)); ?>
  <label>
        ログインID<br>
        <?php echo $this->Form->input('User.email', array(
            'label' => false,
            'placeholder' => ''
        )); ?>
    </label>
    
    <label>
        パスワード<br>
        <?php echo $this->Form->input('User.password', array(
            'type' => 'password',
            'value' => '',
            'label' => false,
            'placeholder' => ''
        )); ?>
    </label>
    <?php echo $this->Session->flash(); // エラーメッセージ ?>
    
    <?php echo $this->Form->submit('ログイン', array(
        'class' => 'input-submit',
        'div' => false
    )); ?>
<?php echo $this->Form->end() ?>

nameにあたる、User.emailの部分はCompnentの設定に合わせる。
ちなみに、formタグにある、'novalidate' => trueは、
何も指定しない場合は勝手に、required="required"がついてしまうので、それを付けない設定を一括で指定している。
バリデーションは他でまとめてしているのでついてないほうがなにかとやりやすい。

まとめ

最初は自前でクッキーなどをチェックしていたのだが、
ページ遷移時の認証機構をcakephpが一括して受け持ってくれるのはかなり楽だった。

参考

カスタム認証オブジェクトの作成 – 認証 — CakePHP Cookbook 2.x ドキュメント
CakePHP 2.4 md5 で Auth認証 – Qiita

   このエントリーをはてなブックマークに追加