問題
ポリシークラス(認証処理)を使って
「権限のないコンテンツ」として403エラーを返したい!
手順
⓪ルートモデルバインディング
①ポリシークラスを作成
②ポリシークラスの中身を編集
③App/Providers/AuthServiceProviderを修正
④web.phpでミドルウェアを使用
⓪ルートモデルバインディング
<a href="{{ route('spending.show', ['id' => $spending['id']]) }}" class="btn btn-info text-white btn-responsive">詳細</a>
↓
↓ // view(index.blade.php)とルーティングのパラメータをモデル名(spending)を合わせる
↓ // リソースコントローラーならターミナルで「php artisan route:list」を叩き、「Name」に記載されている
↓
<a href="{{ route('spending.show', ['spending' => $spending['id']]) }}" class="btn btn-info text-white btn-responsive">詳細</a>
// public function show(int $id)
// ↓ ⭐️修正
public function show(Spending $spending)
{
// ⭐️ルートモデルバインディングのため削除
// user所持か否かはポリシークラスで判別
// $spending = Auth::user()->spending()->find($id);
// ⭐️ルートモデルバインディングのため削除
// URLでユーザーが所持しないidを入力された時に表示
// if(is_null($spending)) {
// abort(404);
// }
return view('original.spending.show', [
'spending' => $spending,
]);
}
public function edit(Spending $spending)
{
// $spending = Auth::user()->spending()->find($id);
// その他処理は省略
}
public function update(CreateData $request, Spending $spending)
{
// $record = Auth::user()->spending()->find($id);
// その他処理は省略
}
public function destroy(Spending $spending)
{
// $record = Auth::user()->spending()->find($id);
// その他処理は省略
}
【解説】
ルートモデルバインディング = ルーティング時点でインスタンスを取得しメソッドに渡す処理
ルーティングにID値を渡すと、find($id)の結果を渡してくれる
↓
要するに
public function show(int $id)
{
$spending = Auth::user()->spending()->find($id);
上記コードのようにshow関数に届いたidをfind()メソッドを使って該当情報を探していた処理を
<a href="{{ route('spending.show', ['spending' => $spending['id']]) }}" class="btn btn-info text-white btn-responsive">詳細</a>
Route::resource('/spending', 'SpendingController');
(リソースコントローラーなので変更なし)
(リソースコントローラーならターミナルで「php artisan route:list」を叩き、「Name」に記載されているバインディングを使う)
public function show(Spending $spending)
{
上記のaタグ→ルーティング→コントローラーの該当箇所を修正すれば
ルーティング時点でfind($id)やってくれるようになっているということ
※注意
現在のユーザー情報は取得していないため
ログインユーザー以外の情報も取得できてしまう。
例えばURLに直接他社ユーザーの情報を入力すると取得できてしまう。
そのためポリシークラスで
$spending = Auth::user()->spending()->find($id);
の、Auth::user()->spending()の部分のような認証処理をつけていく
①ポリシークラスを作成
ポリシークラス = 権限認可の処理を管理する
ターミナル
php artisan make:policy SpendingPolicy --model=Spending
「App/Policies」ディレクトリが生成されて、ファイルも生成される
②ポリシークラスの中身を編集
public function view(User $user, Spending $spending)
{
return $user->id === $spending->user_id;
}
【解説】
ファイル内にはデフォルトで複数の関数がある
閲覧や更新などどのように認可をするか細かく設定ができる
上記はviewにまとめて設定してある
view関数の引数には
・第一引数でUserのインスタンス
・第二引数で対象とするモデル(Spending)のインスタンス
戻り値はbool型
true→許可、false→拒否
③App/Providers/AuthServiceProviderを修正
protected $policies = [
// 'App\Model' => 'App\Policies\ModelPolicy',
'App\Spending' => 'App\Policies\SpendingPolicy',
];
【解説】
$policies内に「モデル=>紐づけるポリシー」で設定
これによって「Spendingモデルに関する処理はSpendingPolicyを通す」ということになる
④web.phpでミドルウェアを使用
//支出ルート以外は省略
Route::group(['middleware' => 'auth'], function() {
// 支出登録
// ⭐️create、storeは、引数でidを必要としないためUser->とSpending->idの一致を確認するポリシークラスで弾かれてしまう
// そのため「create」「store」のみ外側&先に処理する
// ポリシークラスではexceptをしっかり記述
Route::resource('/spending', 'SpendingController', ['only' => ['create', 'store']]);
// ⭐️ポリシークラス
Route::group(['middleware' => 'can:view,spending'], function() {
// 支出登録、支出詳細、支出削除、支出編集
Route::resource('/spending', 'SpendingController', ['except' => ['create', 'store']]);
});
});
【解説 ⭐️ポリシークラス】
canミドルウェア = 引数の認可処理を見て、trueを返せば処理を進め、falseなら403(権限)エラーを返す
view = 今回使用するポリシーの関数
spending = バインディングしているルートのパラメータ
↓
パラメータからモデル(Spending)を判断し、使用するポリシーの関数(view)を参照している