その他
    ホーム 技術発信 DoRuby cakephpで管理機能を実装する

    cakephpで管理機能を実装する

    この記事はアピリッツの技術ブログ「DoRuby」から移行した記事です。情報が古い可能性がありますのでご注意ください。

    今回は、cakephpで開発すると意外と悩む、管理ユーザによる管理機能を実装する際の認証ロジックに関するソリューションを紹介します。

    こんにちは。エンジニアのサンドリバー砂川です。

    2010年も早いもので後3日。弊社も本日29日で、今年の最終営業日となります。

    はじめに

    はじめに、この記事は以下のような仕様のサイトを作ることを想定しています。サイトの一般会員(users)と、管理ユーザ(admin_users)の独立した二種類のアカウントを使用する一般的な認証機能を持つ(マイページ等)マスタデータ等を管理する管理画面を持ち、管理ユーザでログインしなければ閲覧出来ないものとする

    一般的な認証機能 AuthComponent

    cakephpには標準の認証機能として、AuthComponentというコンポーネントが存在します。

    AuthComponentで普通の認証処理を実装するとこんな感じになります。
    app/app_controller.php


    class AppController extends Controller {
      var $components = array('Auth');
      var $uses = array('User');
      var $helpers = array('html', 'Form');
    
      function beforeFilter() {
        parent::beforeFilter();
        $this->layout = 'base';
        $this->setAuth();
      }
    
      function setAuth() {
        # 実際には、AuthComponentの初期値はusersテーブルを使用するので設定は不要。
        $this->Auth->userModel = 'User'; # 認証に使用するアカウントテーブルとしてusersテーブルを使用
        $this->Auth->fields['username'] = 'username'; # IDとしてusernameカラムを使用
        $this->Auth->loginRedirect = '/mypage'; # ログイン成功時にリダイレクトするurl
      }
    }
    

    app_controllerでAuthComponentを設定すると、全てのcontroller処理の前に認証チェックが行われ、beforeFilterに$this->Auth->allow(‘アクション名’);と設定しない限り、ログインしていない場合は/users/loginにリダイレクトされるようになります。

    AuthComponentでは、デフォルトではusersテーブルをアカウントテーブルとして使用しますが、適宜指定することも可能です。今回の実装では、一般機能ではusers、管理機能ではadmin_usersを使用するように切り替えることで実装します

    管理画面実装

    さて、管理画面の実装ですが、cakephpには管理機能を実装する手段としてroutingプレフィックス機能が用意されています。簡単に説明すると通常、cakephpのroutingロジックは以下。


    controller = books, action = admin_add
    ↓
    /books/admin_add
    

    しかし、routingプレフィックスにadminを指定すると一部urlが変化します。


    controller = books, action = admin_add
    ↓
    /books/admin/add
    

    正直この機能は使い辛く、特に一般ユーザと管理ユーザで独立した認証処理を行いたい今回のケースでは使い物になりません。なのでcontrollerごと分離する方式で実装します。

    まず、controllerの共通処理を記述するapp_controller.phpを継承、一般サイト領域の共通処理を記述するbase_controller.phpと、管理機能領域の共通処理を記述するadmin_controller.phpを作成します。
    app/app_controller.php


    class AppController extends Controller {
      var $components = array('Auth');
      var $uses = array('User');
      var $helpers = array('html', 'Form');
    
      # 全体共通処理のみ記述
      function beforeFilter() {
        parent::beforeFilter();
        $this->layout = 'base';
      }
    }
    

    app/controller/base_controller.php


    class BaseController extends AppController {
      var $extra_components = array();
      var $extra_uses = array();
      var $extra_helpers = array();
    
      function __construct() {
        parent::__construct();
        $this->merge_extra();
      }
    
      function beforeFilter() {
        parent::beforeFilter();
        $this->layout = 'base';
        $this->setAdminAuth();
      }
    
      # 一般領域用にusersテーブルをアカウントテーブルとして使用するよう設定
      function setAuth() {
        $this->Auth->userModel = 'User';
        $this->Auth->fields['username'] = 'username';
        $this->Auth->loginRedirect = '/mypage';
      }
    
      function merge_extra() {
        $this->components = array_merge($this->components, $this->extra_components);
        $this->uses = array_merge($this->uses, $this->extra_uses);
        $this->helpers = array_merge($this->helpers, $this->extra_helpers);
      }
    }
    

    app/controller/admin_controller.php


    class AdminController extends AppController {
      var $extra_components = array();
      var $extra_uses = array();
      var $extra_helpers = array();
    
      function __construct() {
        parent::__construct();
        $this->merge_extra();
      }
    
      function beforeFilter() {
        parent::beforeFilter();
        $this->layout = 'admin_base';
        $this->setAdminAuth();
      }
    
      # 管理画面領域用に、admin_usersをアカウントテーブルに使用するよう設定
      function setAdminAuth() {
        $this->Auth->userModel = 'AdminUser';
        $this->Auth->fields['username'] = 'mail_address';
        $this->Auth->loginRedirect = '/admin_top';
      }
    
      function merge_extra() {
        $this->components = array_merge($this->components, $this->extra_components);
        $this->uses = array_merge($this->uses, $this->extra_uses);
        $this->helpers = array_merge($this->helpers, $this->extra_helpers);
      }
    }
    

    一般ユーザが使用するcontrollerはbase_controllerを、管理ユーザが使用するcontrollerはadmin_controllerをそれぞれ継承して使用します。

    また、一般機能領域の共通処理はbase_controller、管理機能領域の共通処理はadmin_controller、全てのcontrollerの共通処理はapp_controllerに、それぞれ記述することにします。
    例:一般controller


    App::import('Controller', 'Base');
    class MypageController extends BaseController {
      var $Name = 'Mypage';
      var $extra_uses = array('Book');
      var $extra_components  = array('RequestHandler');
      var $extra_helpers = array('bk');
    
      function index() {
      }
    }
    

    例:管理controller


    App::import('Controller', 'Admin');
    class BookController extends AdminController {
      var $Name = 'Books';
      var $extra_uses = array('Book');
      var $extra_components  = array('RequestHandler');
      var $extra_helpers = array('bk');
    
      function index() {
      }
    }
    

    こうすることで一般機能領域では一般ユーザ、管理機能領域では管理ユーザでの認証を適用することができます。

    注意点

    この方式の注意点として、通常$usesや$componentsのような各controllerで追加している設定は、代わりに$extra_usesのように追加しなければならない点が挙げられます。これはcakephp内部実装上の理由で以下のような問題が発生するためです。


    App
    $uses = array('A);
    ↓
    Appを継承したAppFoo
    $uses = array('B');
    ↓
    最終的な$uses
    $uses = array('A', 'B'); # $usesや$componentsはマージされて適用される
    
    App
    $uses = array('A);
    ↓
    Appを継承したBase
    $uses = array('B');
    ↓
    Baseを継承したBaseBar
    $uses = array('C');
    ↓
    最終的な$uses
    $uses = array('B', 'C); # Appで指定したAがマージされない。
    

    この問題に対しては、$extra_usesで指定した配列を一旦マージした後、$usesに設定することで対処しています。


    App
    $uses = array('A);
    ↓
    Appを継承したBase
    $uses = array('B');
    ↓
    Baseを継承したBaseBar
    $extra_uses = array('C');
    ↓
    Baseの$usesとBaseBarの$extra_usesをマージ
    $uses = array('B', 'C');
    ↓
    最終的な$uses
    $uses = array('A', 'B', 'C);
    

    以上です。全ての問題点を綺麗に解決出来ているわけではありませんが、管理機能を実装する際に参考になる点もあるかと思います。

    記事を共有
    モバイルバージョンを終了