Laravel4、ロールと許可ベースのルート制御(1)

Tags : Laravel4  

Laravel 4でロールベースの認証を行いたい場合、実用的には既に存在するパッケージを利用するのが便利です。

Sentryは他のフレームワークにも統合されている、人気のある認証システムです。Laravel4用のパッケージの中でも、一番人気です。

Confideは認証、Entrustはロールベースのパーミッション制御を行うパッケージで、同じ作者によるものです。Laravelらしい書き方ができるようです。Sentryほどではありませんが、やはり人気があるパッケージです。

両方共、認証とロールベースのパーミッション制御以外に、パスワードリマインダーなどの機能も提供していますので、認証周りはだいぶ簡単に実現できます。

今回は学習のため、自前でロールベースのURIのアクセス制御を行いましょう。以下の機能に慣れ親しみ目的です。

  • Eloquentの多対多関係
  • ルーティングのBeforeフィルター

最初に、新しくLaravelをインストールしてください。デモのルートページが標示される程度に設定を済ませてください。

続いて、データベースの設定を行いましょう。マイグレーションを使用します。php artisan migrate:installまで、済ませてください。

マイグレーションのやり方に不慣れな方は、以下のミニ・チュートリアルを先に行なってください。今回はマイグレーションの関係などは、詳しく説明しません。

テーブルの準備

今回、5テーブルを使用します。

主なものは、ユーザー認証にも使用するusersテーブル、役割のrolesテーブル、許可のpermissionsテーブルです。

usertsとroles、rolesとpermissionsは、それぞれ多対多で関連付けるため、中間テーブルが2つ必要になります。

今回はACL風のアクセス制御です。あるユーザーは複数の役割を持つことができます。ある役割には複数の許可を設定することができます。URIに対して、必要な許可をチェックするルーティングbeforeフィルターを指定することで、アクセスコントロールを行います。

システムを使用するときには、何でもできるスーバーユーザー権限を用意しておくと、便利です。そのため、役割に"super"を用意し、この権限を持ったユーザーは、全ページにアクセス可能にしましょう。

この程度の規模の制御であれば、データベースを使用するのは大げさです。設定ファイルに用意し、取り込むほうが、スピードでも有利でしょう。あくまで、学習目的です。

まず、マイグレーションをまとめて紹介します。upとdownメソッドの内容です。

usersテーブル

    public function up()
    {
        Schema::create( 'users', function ($table)
            {
                $table->increments( 'id' );
                $table->string( 'username', 30 );
                $table->string( 'password', 64 );
                $table->string( 'email', 320 );
                $table->timestamps();
            } );
    }

    public function down()
    {
        Schema::dorpIfExist('users');
    }

rolesテーブル

    /**
     * Run the migrations.
     *
     * @return void
     */
    public function up()
    {
        Schema::create( 'roles', function($table)
            {
                $table->increments( 'id' );
                $table->string( 'rolename', 20 );
                $table->timestamps();
            } );
    }

    public function down()
    {
        Schema::dropIfExists( 'roles' );
    }

permisssionsテーブル

    public function up()
    {
        Schema::create( 'permissions', function ($table)
            {
                $table->increments( 'id' );
                $table->string( 'allow', 20 );
                $table->timestamps();
            } );
    }

    public function down()
    {
        Schema::dropIfExists( 'permissions' );
    }

主要な3つのテーブルのマイグレーションは、シンプルです。必要なフィールドを定義しているだけです。

続いて、残りの中間テーブルを作成しましょう。まずはusersとrolesの中間テーブルです。

中間テーブルの名前は、それぞれのテーブルの名前の単数形をアルファベット順に、アンダーバーでつないだものです。ですから、role_userが中間テーブルになります。もちろん、この名前はデフォルトの規約に従った場合です。規約に従わず、自由に名前を付けることも可能です。その場合、Eloquentモデル中で行う、関連付けの定義時に、明示的にそのテーブル名を指定する必要があります。

role_userテーブル

    public function up()
    {
        Schema::create( 'role_user', function($table)
            {
                $table->increments( 'id' );
                $table->integer( 'user_id' )->unsigned();
                $table->integer( 'role_id' )->unsigned();
            } );
    }

    public function down()
    {
        Schema::dropIfExists( 'role_user' );
    }

中間テーブルのフィールドは、それ自身のID、usersテーブルのIDを表すuser_id、rolesテーブルのIDを表すrole_idです。テーブルの単数名に_idを付けたものになります。

incrementsメソッドで作成されたフィールドは符号なし整数です。ですから、user_idとrole_idはintegerメソッドで作成し、unsignedメソッドで符号なしを指定します。

同じ規約に従い、rolesテーブルと、permissionsテーブルの中間テーブルも作成しましょう。

permission_roleテーブル

    public function up()
    {
        Schema::create( 'permission_role', function($table)
            {
                $table->increments('id');
                $table->integer('permission_id')->unsigned();
                $table->integer('role_id')->unsigned();
            } );
    }

    public function down()
    {
            Schema::dropIfExists('permission_role');
    }

モデル作成

続いて、データベースに初期値を設定するため、Eloquentモデルを作成しましょう。

データベースに初期値設定するためには、外部ツールを使用することも可能ですし、使用するデータベースのコマンドラインツールを使用することもできます。今回は、Laravelのシーディングを使用して、設定します。

シーディングは、テーブルの初期値設定のことです。Eloquentを使用せずとも、クエリービルダーを使ったり、直接SQLを指定したりすることも可能ですが、やはりLaravelではEloqunetが花ですから、いろいろ使用して、慣れましょう。

関連付けも、行なっていきます。

最初にUser.phpです。User.phpは予めインストール時に用意されます。以下の2メソッドを付け加えてください。

User.php

    public function setPasswordAttribute( $value )
    {
        $this->attributes['password'] = Hash::make( $value );
    }

    public function roles()
    {
        return $this->belongsToMany( 'Role' );
    }

setPasswordAttributeメソッドは、保存時に毎回ハッシュをかけるのは面倒なため、passwordフィールドを設定する時は、毎回自動的にハッシュされるように、設定しています。

rolesメソッドはリレーションの名前でもあります。どんな名前でもメソッド名として使用できますが、多対多関係の場合、相手のテーブル名と同じ、複数形がしっくりするでしょう。

このメソッドでは、相手のモデル名を指定し、belongsToManyメソッドを返します。ポイントとして、rolesはリレーション名でもあり、関係付の定義では、相手のモデル名(クラス名)を指定することを押さえておきましょう。

続いて、roleモデルです。これは短いですので、全コードを紹介します。

Role.php

<?php

class Role extends Eloquent
{

    public function Users()
    {
        return $this->belongsToMany( 'User' );
    }

    public function Permissions()
    {
        return $this->belongsToMany( 'Permission' );
    }

}

rolesテーブルは、usersテーブルとpermissionsテーブルに対し、それぞれ多対多の関係を持つため、二つのリレーションを定義しています。

Permission.php

<?php

class Permission extends Eloquent
{

    public function Roles()
    {
        return $this->belongsToMany( 'Role' );
    }

}

permissionsテーブルは、rolesテーブルに対してのみ関係付けを持ちますから、定義は一つだけです。

続き: