はじめに
例えば、あるサイトでユーザーID1の人がマイページのユーザー詳細画面を開いた時、URLが
https://○○○○/user/1
のような形だったとします。1というのはユーザーIDを指します。
もしここを2に変えた時、何が起こると思いますか?
なんと、ユーザーID1の人がユーザーID2の人のユーザー詳細画面に入れてしまうんです!
個人情報が全て筒抜けになってしまうんです。他にも、ユーザー情報の編集画面に入って他人の情報を書き換えることもできてしまったりします。
そんな危ないサイト使いたくないですよね。
なので、このような場合は必ず認可機能を実装してアクセス制限を行うようにしましょう!!
※IDを推測されやすい番号にしない、といった内容は今回の記事では取り扱わないことにします。
環境
- Composer 1.10.20
- PHP 7.4.15
- Laravel 6.20.20
Policyとは
Laravelでは認可のために方法がいくつか用意されています。
今回はその中からPolicyについてのみ説明しますが、詳しく知りたい方は以下の記事を読んでみてください。
Policyとは、ある特定のモデルに対して行うアクション(作成、更新、削除、閲覧等)に関してアクセス制限を行う仕組みのことです。そのためモデルごとに個別の独立したPolicyファイルを作成する必要があります。
実装
ここから、実装の手順を説明していきます。
1. Policyの作成
まず、Policyファイルを作成します。
–-model
オプションでモデルを指定してPolicyを作成をすると、Policyで使用するメソッドが記述された状態でファイルを作成することができます。
今回はUserモデルに対してPolicyを生成します。
$ php artisan make:policy UserPolicy --model=User
これを実行すると、app/Policies
ディレクトリの中にUserPolicy.php
が作成されます。
namespace App\Policies;
use App\User;
use App\User;
use Illuminate\Auth\Access\HandlesAuthorization;
class UserPolicy
{
use HandlesAuthorization;
/**
* Determine whether the user can view any users.
*
* @param \App\User $user
* @return mixed
*/
public function viewAny(User $user)
{
//
}
/**
* Determine whether the user can view the user.
*
* @param \App\User $user
* @param \App\User $user
* @return mixed
*/
public function view(User $user, User $user)
{
//
}
}
※本来は他にcreate, update, delete, restore, forceDeleteのメソッドが記述された形で作成されますが、省略しています。
今回はviewメソッドを使用しますが、当時僕はここで「ん?」となりました。
User $user, User $user
ってどういうこと?同じじゃないの??
ここの説明と記述は後ほどします。
###2. Policyの登録
Policyを作成したら、登録する必要があります(省略可※後述)
app/Providers
ディレクトリ内のAuthServiceProvider.php
を編集します。
protected $policies = [
'App\User' => 'App\Policies\UserPolicy',
];
※ Policyの自動検出
Laravel 5.8 で追加された機能です。
モデルとポリシーの標準命名規則に従っているポリシーをLaravelが自動的に見つけてくれる便利な機能で、この場合はPolicyの登録を省略できます。
例えば、app
ディレクトリ下にモデルがある場合、app/Policies
ディレクトリ下のPolicyファイルを自動検出してくれます。
この時、モデルの名前に対応させたPolicyファイルを作成する必要があります。
(今回の場合はUserモデルに対するPolicyなので、UserPolicy.php
という名前をつけます。)
3. コントローラーの編集
次に、マイページ表示の処理を行っているshowアクションにauthorize
メソッドを追記していきます。
public function show(User $user)
{
$this->authorize('view', $user);//追記
return view('mypage');
}
authorize
メソッドの最初の引数をview
、2番目の引数をUserモデルの変数$user
とすることで、UserPolicy.php
のview
メソッドに$user
の情報を渡します。
アクションが認可されない場合、authorize
メソッドは403エラーを返します。
4. Policyの編集
UserPolicy.php
に戻って、view
メソッドを編集していきます。
public function view(User $user, User $user)
{
return $user->id === $user->id
}
ここで、先ほどお話しした「User $user, User $user
ってどういうこと?」について説明します。
この2つの$user
にはちゃんと違いがあります。1つ目の$user
にはこのviewメソッドにて取得されたユーザーの情報が入っていて、2つ目の$user
にはコントローラーのshowアクションから送られてきたユーザーの情報が入っています。
このままだと分かりづらいので、2つ目の方を$request_user
に変更しておきましょう。
public function view(User $user, User $request_user)
{
return $user->id === $request_user->id;
}
完成!
これで機能としては完成しましたが、処理の流れを簡単に説明したいと思います!
IDが1のユーザーがログインしている状態と仮定して、
https://○○○○/user/1
というURLを
https://○○○○/user/2
に変更してリロードした際の処理を説明します。
####処理の流れ
- コントローラーのshowアクションで、UserモデルからユーザーID2の情報を
$user
として取得。 -
$this->authorize('view', $user);
で、ユーザーID2の情報をUserPolicy.php
に渡す。 -
UserPolicy.php
にて、Userモデルから本来のユーザーID1の情報を$user
として取得、$request_user
にはコントローラーから送られた$user
(今回はユーザーID2の情報)の値が入っている。 -
return 1 === 2;
となるのでfalseを返し、403エラーを表示する。
##最後に
以上、Laravelにおける認可機能の一部をご紹介しました!
初学者あるあるなのかなと思いますが、書いたコードをいざ言葉で説明するとなると、それが意外と難しいことに気づきました。
僕も初学者なので、初学者でも分かりやすい言葉で説明できるように意識したいと思います。
認識が間違っている点があれば、ぜひコメントをお願いいたします!!