Laravelではユーザーによって表示や動作を変えることができます。
そのための方法として、Gate, Policy, Middleware の3つがありますが、違いが分かりにくいですよね。
公式サイト(英語)での解説を元に、3つの方法の比較と使い方を紹介します。
Laravelで権限を設定するためのGate, Policy, Middlewareの3つ方法を比較
まずはGate, Policy, Middlewareの比較をしてみましょう。
それぞれの違いは次のとおり。
【Middleware, Gate, Policyの比較】
難易度 | 特徴 | |
Gate | ★ | 特定の動作を制限 |
Policy | ★★★ | 特定のモデルを制限 |
Middleware | ★★ | ルート設定の段階で制限 |
難易度は、個々のコードによります。
上記は目安としてとらえてくださいね。
ひとことで用途を言うと、こうなります。
- Gateは簡単に単独で制限をかけたいときに使う
- Policyはモデルごと、リソースコントローラーごとなど、まとめて制限をかけたいときに使う
- Middlewareはルート設定の段階で制限をかけたいときに使う
ひとつのプロジェクトの中で、3つの方法を組み合わせながら使っていけます。
それぞれの方法の具体的な使い方を解説していきますね。
LaravelのGateを使った処理の方法
Gateは、入ってきた人を通すための門(ゲート)の役割を果たします。
設定方法はわりとシンプル。
今回は投稿者本人のみ、投稿のアップデートを実行できるようにするためのGateを作ってみます。
Gateの設定方法
App/Providersの中のAuthServiceProviderファイルで設定します。
ファイル上部に、つぎのふたつのモデルのuse宣言を入れておきます。
1 2 |
use App\Models\Post; use App\Models\User; |
実際の設定はbootメソッドに入れます。
1 2 3 4 5 6 7 |
public function boot() { $this->registerPolicies(); Gate::define('poster', function(User $user, Post $post) { return $user->id==$post->user_id; }); } |
posterというGateを定義。
ユーザーが投稿者と同じであれば、Gateをくぐれることにする。
Gateを使ったコントローラー例
上記のGateを使って、たとえばユーザーが投稿者本人であった場合のみ、update処理ができるよう設定します。
PostControllerのupdateメソッドに下記のように入れるだけでOK。
1 2 3 4 5 6 7 |
public function update(Request $request, Post $post) { if(! Gate::allows('update-post', $post)){ abort(403); } // アップデートの処理 } |
‘update-post’ ゲートを許可されていなければ、403を返す。
ユーザー以外の人がアップデートを実行しようとすると、403画面が表示されます。
制限をビューに適用
なお作成したGate制限はビューにも適用できます。
Gateのposter制限を適用するには、ビューファイル(blade.php)の中で制限をかけたい部分を@can~@endcanで囲みます。
1 2 3 |
@can('poster',$post) 制限をかけたい部分 @endcan |
Gateをくぐったユーザーにしか「制限をかけたい部分」は表示されません。
LaravelのPolicyを使った処理の方法
Policyは、特定のモデルやリソースコントローラーに対して、まとめてルールを設定します。
Gateよりも面倒ですが、ひとまとめにルールを設定したいときには便利。
PolicyでもGateで行ったと同様、今回は投稿者本人のみ、投稿のアップデートを実行するコードを作ってみます。
Policyの設定方法
Policyは新規Policy作成、Policy登録、Policyファイル編集、コントローラーに登録という4つのステップを踏んでいきます。
Gateより手順が多くなります。
①新規Policy作成
まずは次のコマンドで、新規Policyを作成します。
1 |
php artisan make:policy PostPolicy --model=Post |
上記のようにモデル名をいれておくと、最初からメソッド名が入ったPolicyを作れます。
②Policyの登録
作成したPolicyは、App/Providersの中のAuthServiceProviderファイルのprotected $policiesに登録します。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 |
class AuthServiceProvider extends ServiceProvider { /** * The policy mappings for the application. * * @var array */ protected $policies = [ App\Models\Post::class=>App\Policies\PostPolicy::class, ]; /** * Register any authentication / authorization services. * * @return void */ public function boot() { $this->registerPolicies(); } } |
③Policyファイルの編集
つぎにPolicyファイルを設定します。
App/Policiesの中のPostPolicy.phpファイルを開きます。
先頭にモデル名をuse宣言でいれておきます。
1 2 |
use App\Models\Post; use App\Models\User; |
メソッド名は、リソースコントローラーのデフォルトルールに従って作成されています。
今回はupdateの部分に、次のようにいれておきます。
1 2 3 4 |
public function update(User $user, Post $post) { return $user->id==$post->user_id; } |
④コントローラーの編集
PostControllerのupdateメソッドには次のようにいれておきます。
1 2 3 4 5 6 7 8 |
public function update(Request $request, Post $post) { if($request->user()->cannot('update', $post)){ abort(403); } // アップデートの処理 } |
ユーザーが’update’ ができないなら、403を返す。
もっと短く、下記でもOKです。
1 |
$this->authorize('update', $post); |
ユーザー以外の人がアップデートを実行しようとすると、403画面が表示されます。
制限をビューに適用
なお作成したPolicy制限はビューにも適用できます。
Postのupdateポリシーを適用するには、ビューファイル(blade.php)の中で制限をかけたい部分を@can~@endcanで囲みます。
1 2 3 |
@can('update', $post) 制限をかけたい部分 @endcan |
Policyに一致したユーザーにしか「制限をかけたい部分」は表示されません。
Policyのメリット
これだけ見ると、「Policyってめんどくさ!」って思いませんか?
正直、わたしもそう思います。
今回の例のように投稿者本人しか編集できなくするなら、Gateを作ったほうが簡単に制限をかけられます。
ただPolicyは、モデルやリソース全体に制限をかけられるのが魅力。
さきほどはPolicyファイルにupdateメソッドしか設定しませんでしたが、すべてのメソッドに制限をいれることも可能。
そのうえで、PostControllerの上部にコンストラクタを設定してしまえば、メソッドごとにPolicyについて書く必要はなくなります。
1 2 3 4 |
public function __construct() { $this->authorizeResource(Post::class, 'post'); } |
リソースコントローラー利用時にPolicyを適用させる方法はこちら。
LaravelのMiddlewareを使った処理の方法
最後にLaravelのミドルウェアを使った処理を紹介します。
ミドルウェアとは訪問者のリクエストに対して、ルーターとコントローラーの間にたって処理を行うメカニズム。
つまりコントローラーでの処理を行うより前に、ミドルウェアが入ってきて、動作を制御できるのです。
単独で使えば、ログイン認証のような機能に使えます。
つまりログイン機能を入れた時点で、既にMiddleware機能を使っているんですね。
GateやPolicyと組み合わせれば、ルート設定に手軽にGate,Policyのルールを適用できます。
先ほど作ったPostのupdateポリシーをルート設定に適用するには、ルート設定ファイル(web.php)に次のように入れるだけ。
1 2 |
Route::put('/post/{post}', 'PostController@update', function (Post $post) { })->middleware('can:update,post')->name('post.update'); |
投稿者本人でない人がupdateを行おうとすると、403エラーとなります。
ミドルウェアの考え方や、いちから作る方法は他の記事で詳しく書いています。
さいごに
今回はLaravelでの制限のかけ方を解説しました。
使い分けのポイントはこちら。
今回のまとめ
- 簡単に制限をかけるならGate
- モデルごと、リソースコントローラーごとならPolicy
- ルート設定にかけるときはMiddleware
なお今回はUserとPostを使って例を紹介しました。
こういった処理を行うには、あらかじめ、UserモデルとPostモデルの間にリレーションを設定し、$postテーブルの中に$user_idを入れておく必要があります。
リレーションについては、別記事を参考にしてくださいね。