🚀

【Laravel6】Gate機能を使って権限機能を簡単に実装する

2021/01/25に公開

はじめに

Laravel6で権限機能を実装したのでその方法についてまとめます。

権限機能と言えば

  • 「一般ユーザー」と「管理者」でアクセスできるページや操作可能な機能を制限する
  • 「管理者」の中でグレード(種別)を設定して管理画面上で色々と制限をかける権限機能

が主にあるかなと思いますが、この記事では後者を例にします。
(とはいえ、前者でも基本的な考え方は同じだと思います)

環境

Laravel 6.8

設定する権限

記事用に仮想的な権限を簡易的に設定します。

権限名 内容
最強 全ての機能を使える
普通 一部機能を使える
最弱 1つの機能しか使えない

こんな感じで、権限の強さによって使える機能を限定します。

権限機能の実装方法

手順はざっくりこんな感じです。
(順番を入れ替えても問題なく実装できます)

  1. 各ユーザーに権限を設定する
  2. 権限用データ作成
  3. Gate機能で権限を定義する
  4. ルーティングに権限情報を追加する
  5. (補足)ユーザーの権限によってViewの表示を変える

1.各ユーザーに権限を設定する

まずは各ユーザーに権限を設定する必要があるのでマイグレーションファイルを修正してDBを仕様を変えます。
ユーザーに権限を持たせる方法としては

  • Usersテーブル(テーブル名は任意)に直接権限情報を持たせる
  • 権限用のテーブルを作成してUsersテーブルに外部キーでリレーションさせる

の方法があると思いますが、僕は後者の方が今後管理しやすいので後者で説明します。

以下コマンドで権限用テーブルを作成します。

$ hp artisan make:migration create_permissions_table —-create=permissions

以下のように修正します。

database/migrations/****_**_**_******_create_permissions_table.php
<?php

use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;

class CreatePermissionsTable extends Migration
{
    /**
     * Run the migrations.
     *
     * @return void
     */
    public function up()
    {
        Schema::create('permissions', function (Blueprint $table) {
            $table->increments('id')->comment('権限ID');
            $table->char('name', 10)->comment('権限名');
            $table->timestamps();
        });
    }

    /**
     * Reverse the migrations.
     *
     * @return void
     */
    public function down()
    {
        Schema::dropIfExists('permissions');
    }
}

既存のUsersテーブルに権限用のカラムを追加するので以下コマンドを実行

$ php artisan make:migration add_permission_id_column_to_users_table —-table=users
database/migrations/****_**_**_******_add_permission_id_column_to_users_table.php
<?php

use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;

class AddPermissionIdColumnToUsersTable extends Migration
{
    /**
     * Run the migrations.
     *
     * @return void
     */
    public function up()
    {
        Schema::table('users', function (Blueprint $table) {
            $table->integer('permission_id')->unsigned()->comment('権限ID')->after('address3');
            $table->foreign('permission_id')->references('id')->on('permissions')->onDelete('cascade');
        });
    }

    /**
     * Reverse the migrations.
     *
     * @return void
     */
    public function down()
    {
        Schema::table('users', function (Blueprint $table) {
            $table->dropForeign('users_permission_id_foreign');
        });
    }
}

開発の始めから権限も盛り込んだ計画にしていれば、usersテーブル作成用のマイグレーションファイルに権限用のカラムを追記すればOK。

2.権限用データ作成

以下コマンドでシーダーファイルを作成。

$ php artisan db:seed --class=PermissionTableSeeder

シーダーファイルを修正。

database/seeds/PermissionTableSeeder.php
<?php

use Illuminate\Database\Seeder;
use Illuminate\Support\Facades\DB;

class PermissionTableSeeder extends Seeder
{
    /**
     * Run the database seeds.
     *
     * @return void
     */
    public function run()
    {
        DB::table('permissions')->insert([
            [
                // id = 1
                'name' => '最弱',
                'created_at' => date('Y-m-d H:i:s'),
                'updated_at' => date('Y-m-d H:i:s'),
            ],
            [
                // id = 2
                'name' => '普通',
                'created_at' => date('Y-m-d H:i:s'),
                'updated_at' => date('Y-m-d H:i:s'),
            ],
            [
                // id = 3
                'name' => '最強',
                'created_at' => date('Y-m-d H:i:s'),
                'updated_at' => date('Y-m-d H:i:s'),
            ],
        ]);
    }
}

これで権限の仕様としてはこんな感じになりました。

権限名 内容 権限ID
最強 全ての機能を使える 1
普通 一部機能を使える 2
最弱 1つの機能しか使えない 3

あとはUsersテーブルに権限IDを持たせれば良いです。
というわけでUsersテーブル用のシーダーファイルにPermissionsテーブルの情報を持たせるので修正します。

database/seeds/UsersTableSeeder.php
<?php

use Illuminate\Database\Seeder;

class UsersTableSeeder extends Seeder
{
    /**
     * Run the database seeds.
     *
     * @return void
     */
    public function run()
    {
        DB::table('users')->insert([
            [
                //略
                'permission_id' => 1,
                'created_at' => date('Y-m-d H:i:s'),
                'updated_at' => date('Y-m-d H:i:s'),
            ],
            [
                //略
                'permission_id' => 2,
                'created_at' => date('Y-m-d H:i:s'),
                'updated_at' => date('Y-m-d H:i:s'),
            ],
            [
                //略
                'permission_id' => 3,
                'created_at' => date('Y-m-d H:i:s'),
                'updated_at' => date('Y-m-d H:i:s'),
            ],
        ]);
    }
}

3.Gate機能で権限を定義する

Laravelで権限機能を実装する場合はGate機能を使います。

参考:ReaDouble:Laravel 6.x 認可

app/Providers/AuthServiceProvider.phpを修正します。

app/Providers/AuthServiceProvider.php
<?php

namespace App\Providers;

use Illuminate\Foundation\Support\Providers\AuthServiceProvider as ServiceProvider;
use Illuminate\Support\Facades\Gate;

class AuthServiceProvider extends ServiceProvider
{
    //略

    /**
     * Register any authentication / authorization services.
     *
     * @return void
     */
    public function boot()
    {
        $this->registerPolicies();

        // 「最強」だけに適用
        Gate::define('saikyou_only', function ($user) {
            return ($user->permission_id == 1);
        });

        // 「最強」と「普通」に適用
        Gate::define('saikyou_and_futsuu', function ($user) {
            return ($user->permission_id <= 2);
        });

        // 「最強」と「普通」と「最弱」全てに適用
        Gate::define('all', function ($user) {
            return ($user->permission_id <= 3);
        });
    }
}

この記事では簡易的にsaikyou_onlyとかsaikyou_and_futsuuにしてますが実際に定義する時はちゃんとした英語の方が良いですw

4.ルーティングに権限情報を追加する

app/Providers/AuthServiceProvider.phpに定義した権限をルーティングに適用します。

routes/web.php
<?php

use Illuminate\Support\Facades\Route;

//略

// 「最強」のユーザーしか使えない機能
Route::group(['middleware' => ['auth', 'can:saikyou_only']], function () {
    //処理
});

// 「最強」と「普通」のユーザーが使える機能
Route::group(['middleware' => ['auth', 'can:saikyou_and_futsuu']], function () {
    //処理
});

//全てのユーザーが使える機能
Route::group(['middleware' => ['auth', 'can:all']], function () {
    //処理
});

これで権限が与えられてないユーザーが該当のURLにアクセスすると403エラーになります。

(補足)5.ユーザーの権限によってViewの表示を変える

「ログインしているユーザーの権限によってこのボタンを非表示にしたい!」なんてこともあるかと。

そんな時はViewファイルをこんな風に書きます。

****.blade.php
@can('saikyou_only')
    <!-- (表示したい要素) -->            
@endcan

これで権限が**「最強」(permission_id=1)のユーザーでログインした時のみ表示される**要素にできます。

参考記事

さいごに

実装するまで「権限機能ってなんか複雑そうだな...」と思いましたが、(意外と)簡単に実装することができました。

Discussion