LaravelのGate機能を使って権限機能を実装する

ゆーたろー

こんにちは、ゆーたろーです。

大阪のHRTechベンチャーのエンジニアです。
TypeScript/Vue.js(Nuxt.js)/Laravelを使っています。

・プログラミングスクール講師
・月1で勉強会運営
・Twitter(フォロワー4700人以上)で情報発信

など色々やっている1児のパパです。

この記事ではLaravelで権限機能を実装する方法を解説します。

さすがフレームワーク!という感じで結構簡単に実装できますよ。

目次

バージョン

Laravel 6.8

権限機能について知ろう

権限について知ろう

Laravelで実装する前にまずは権限機能とは何なのか?を抑えておきましょう。

既にご存知の方はここは読み飛ばしてもらって問題ありません。

権限機能とは

ユーザーの種別によって遷移可能なページや操作可能な機能を制限する機能

のことです。

ユーザーと一言でいっても

  • 一般ユーザーと管理者という区別
  • 同じ一般ユーザー、同じ管理者の中でグレードを分けて区別

などがあるので適用範囲は結構広いです。

プログラミング関連で身近な例で言うとプログラミング経験者はお馴染みのProgateで無料会員と有料会員という会員のグレードの違いで閲覧できるコンテンツを制御していることです。

このように権限機能はWebアプリ、スマホアプリで一般的に使われています。

権限機能について理解したところでLaravelで実装する方法を解説しますね!

実装する権限の概要(一例です)

一例として以下のような仮想の権限を考えます。。

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

3つの権限の強さによって使うことのできる機能を限定するような処理を解説します。

Laravelで権限機能を実装する方法

初めに全体の流れを把握できるように手順はざっくり書きます。

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

LaravelではGate機能という便利な機能があり、それを用いて権限機能を実装します。

上記順番は入れ替えても問題なく実装できますが今回はこの流れにします。

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

初めにユーザーに権限を設定する必要があるので、マイグレーションファイルを修正してテーブルの仕様を変えます。

ユーザーに権限を持たせる際のテーブルの仕様は大きく2つあります。

  • ユーザー用テーブルに直接権限のマスターデータを持たせる
  • 権限用のテーブルを作成しマスターデータを定義、Usersテーブルに外部キーでリレーションさせる

権限の種別が増えた後でも管理しやすい点で、この記事では別テーブルで権限情報を保持する後者の方法で解説します。

以下コマンドで権限用テーブル(permissions)を作成します。
(ユーザー用のテーブルは既にあるものとします)

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

マイグレーションファイルが作成されたら修正します。
ID(idカラム)とそれに対応する権限の名前(nameカラム)だけの単純なものです。

<?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')->index()->comment('権限ID');
            $table->char('name', 10)->comment('権限名');
        });
    }

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


->index()というメソッドでIDカラムにはインデックスをつけています。

インデックス自体は権限機能とは直接関係はありませんが、こちらがわかりやすかったので参考にしてください。

次に、既存のユーザー用のテーブル(users)に権限用のカラム(外部キー)を追加するためのマイグレーションファイルを作成します。

$ php artisan make:migration add_permission_id_column_to_users_table —-table=users

今回はusersテーブルが既に完成していて後から権限用の外部キー(FK)を追加するためにこのように専用のマイグレーションファイルを作成しています。

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

作成したら修正します。

<?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');
        });
    }
}

これで各ユーザーが権限情報を持つことができるようになりました。

権限用のマスターデータ作成

マスターデータはシーダーファイルを使うので、以下コマンドを実行してファイルを修正します。

$ php artisan db:seed --class=PermissionTableSeeder
<?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' => '最弱',
            ],
            [
                // id = 2
                'name' => '普通',
            ],
            [
                // id = 3
                'name' => '最強',
            ],
        ]);
    }
}

これで権限の仕様としては以下の表のようになります。

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

あとはusersテーブルに外部キーで権限IDを持たせれば良いので、usersテーブル用のシーダーファイルを修正します。

<?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' => now(),
                'updated_at' => now(),
            ],
            [
                // 他のユーザー情報は略
                'permission_id' => 2,
                'created_at' => now(),
                'updated_at' => now(),
            ],
            [
                // 他のユーザー情報は略
                'permission_id' => 3,
                'created_at' => now(),
                'updated_at' => now(),
            ],
        ]);
    }
}

これで最強、普通、最弱の権限を持つユーザーができました。

Gate機能で権限を定義する

Laravelで権限機能を実装する場合はGate機能を使います。(公式ドキュメント

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とかにしてますが実際に定義する時はもう少しマシな名前にしてくださいね!笑

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

先ほどの手順でapp/Providers/AuthServiceProvider.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 () {
    //処理
});

この記述でそれぞれのルーティングに権限による制限を付与することができます。

['middleware' => ['auth', 'can:saikyou_only']]

あるユーザーが自分の権限では遷移できない画面や実行できない処理のURLにアクセスすると403エラーとなります。

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

アプリケーション開発をしていると、『ログインしているユーザーの権限によってこのボタンの表示/非表示を切り替えたい!』なんてこともあるかと思います。

そんな時はViewファイル(画面用のファイル)を以下のように書くことで実装することでできます。

@can('saikyou_only')
    <!-- (表示したいボタンやリンクなどの要素) -->            
@endcan

このように@can@endcanを使うことで権限が最強(permission_id=1)のユーザーでログインした時のみ表示される要素にできます。

最後に

Laravelで権限機能を実装する方法を解説しました。

僕自身、仕事で実装するまで『権限機能ってなんか複雑そうだな…』と思っていましたが、結構簡単に実装することができました。

さすがフレームワーク!という感じですね。

“頭おかしい”プログラミングスクール『やんばるエキスパート』

内定者続出のエンジニア転職特化オンラインコミュニティ『転職クエスト』

この記事が気に入ったら
フォローしてね!

よかったらシェアしてね!

この記事を書いた人

上場グループのHRTechベンチャーで働くWebエンジニアです。
新卒で入社した大手重工メーカを4年で退職し、2020年4月からエンジニアとキャリアチェンジしました。

仕事ではTypeScript/Vue.js(Nuxt.js)/Laravelを主に使っています。

プログラミングスクールの講師やデザイン関連のお仕事もさせてもらっています。

神戸で「つながる勉強会」という勉強会を月1で運営しています。
https://tsunagaru-kobe.connpass.com/

お仕事のご依頼、ご相談はお問い合わせページもしくはTwitterのDMからお願いします。

コメント

コメントする

目次
閉じる