1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

[Laravel6.x]認可について ~備忘録~

Posted at

はじめに

この記事はプログラミング初学者による備忘録用の記事であり、また、少しでも他の初学者のお役に立てればと思い書いています。

今回は、Laravelの認可について色々と学んだので、すぐに見返すことができるように備忘録としてまとめておきたいと思います。

間違いなどがございましたら、ご指摘のほどよろしくお願い致します。

"認可"とは

認可には、認証に基づく認可と認証から切り離した認可が存在します。
大抵の場合、認証に基づく認可であるケースが多い為、認証と認可は依存し合っているというイメージが強いと思われます。

認証に基づく認可とは

Laravelにおける認可とは、特定の条件に対して、どの様な動作を許可するかを定義することを指します。
要するに、認証したユーザに対して、具体的にどの様な動作を許可するかを定義するということです。

公式ドキュメントでは、特定のリソースに対するユーザーアクションを認可する簡単な手法と書かれています。

上記のように、「特定の条件」が「特定の誰かであることが認証されている」という条件であることが多い為、認可は認証に依存しているケースが多くなっています。

認証から切り離した認可とは

認証から切り離した認可を考える際によく挙げられる例として、「チケットによる認可」があります。

チケットによる許可で重要なのは、特定の条件(誰か)であるから特定の動作をする許可を与えるのではなく、チケットを持っていることで特定の動作をする許可が与えられるという点です。
特定の条件(特定の誰か=対象)とは関係なく、チケットを持っていることで特定の動作をする許可が与えられるということが、認証から切り離した認可となります。

Laravelで"認可"を設定する方法

公式ドキュメントによると、Laravelの認可のアプローチはシンプルで、主に2つの認可アクションの方法としてGatePolicyというものが存在すると書かれています。

・Gateとは

Gateとは、公式ドキュメントで下記のように書かれています。

ゲートは、特定のアクションを実行できる許可が、あるユーザーにあるかを決めるクロージャのことです。
通常は、App\Providers\AuthServiceProviderの中で、Gateファサードを使用し、定義します。
Laravel6.x 認可 ゲート

特徴
・Gateを使用してアクションを認可する際は、メソッドに現在認証中のユーザーを渡す必要がない
・単純な論理値を返すGateだけではなく、エラーメッセージを含む詳細なレスポンスを返せる
・特定のモデルに関連していないユーザのアクションに対してアクセス制限を与える時に使用するケースが多い
などが挙げられます。

重要
公式ドキュメントでも記載されているように、主にモデルやリソースとは関連しないアクションに対して、Gateは適用されます。
要は、GateとPolicyを使い分けて認可を定義するということです。

定義の仕方

GateはApp\Providersフォルダの下にあるAuthServiceProvider.phpの中にアクセス制限を行うための定義を記述します。
定義を行う時はbootメソッドの中にGateファサードのdefineメソッドを使用して定義します。

AuthServiceProvider.php
class AuthServiceProvider extends ServiceProvider
{
//略
public function boot()
{
$this->registerPolicies();

Gate::define('isAdmin', function ($user) {
        return $user->role == 'administrator';
    });
}
}

上記の例のように、defineメソッドの第一引数にはアクセス制限を行う際に利用する任意の名前、第二引数にはクロージャ―を設定します。
クロージャ―では基本的にtrueかfalseかを戻します。上記の例では、roleがadministratorであるかどうかを判別する処理を記述し、roleがadministratorであればtrueを戻し、異なればfalseを戻します。

定義を行なった後に、アクセス制限を行いたい箇所に追加でコードを記述していきます。

Gateで定義した認可の使用方法

・bladeファイルによるアクセス制御
Gateの設定後にbladeファイルでアクセス制限を行う場合は、@canや@cannotを利用します。

example.blade.php
@can('isAdmin')
// ユーザ一覧を表示するhtmlを記述
@elsecan
<p>管理者のみユーザ一覧を閲覧できます。</p>
@endcan

//or

@cannot('isAdmin')
// ユーザ一覧を表示するhtmlを記述
@elsecannot
<p>管理者以外がユーザ一覧を閲覧できます。</p>
@endcannot

・コントローラーによるアクセス制御
コントローラーの中でアクセス制限を行う場合は、authorizeメソッド、allowsメソッド、deniesメソッド等を利用します。

ExampleController.php
namespace App\Http\Controllers;
use Illuminate\Http\Request;
use App\User;
use Gate;

class UserController extends Controller
{
//allowsの場合(deniesはallowsと逆の処理を行う)
if (Gate::allows('idAdmin')) {
    $users = User::all();
}
else{
dd('管理者のみユーザー一覧を表示できます。');
}
return view('users.index',compact('users'));

//authorizeの場合
public function index(){

Gate::authorize('isAdmin');

$users = User::all();

return view('users.index',compact('users'));
}
}

補足
・allowsメソッドに現在認証中のユーザーを渡す必要はなく、Laravelが自動的にGateクロージャにユーザーを渡します。
・authorizeメソッドを使用した場合、条件が不一致の際は自動的に403のAuthorizationExceptionがthrowされます。

他にも、特定のユーザーがあるアクションを実行できる認可を持っているかを確認するメソッドや、複数のアクションの許可を一度に指定できるメソッドなどもある為、詳しくは公式ドキュメントをご確認ください。

・Policyとは

Policyとは、公式ドキュメントで下記のように書かれています。

ポリシーは特定のモデルやリソースに関する認可ロジックを系統立てるクラスです。
Laravelドキュメント 認可 ポリシー作成

特徴
・Policyでは特定のモデルに関連する処理に対してアクセス制限を行ないます。

重要
全てのポリシーはLaravelのサービスコンテナにより依存解決されるため、ポリシーのコンストラクタに必要な依存をタイプヒントすれば、自動注入されます。

定義の仕方

make:policy Artisanコマンドを使用し、ポリシーを生成できます。生成したポリシーはapp/Policiesディレクトリに設置されます。下記コマンドを実行して下さい。

php artisan make:policy ExamplePolicy

基本的な「CRUD」ポリシーメソッドを生成するクラスへ含めたい場合は、コマンド実行時に--modelを指定してください。

php artisan make:policy ExamplePolicy --model=Example

コマンド入力後、下記のようなクラスを作成して下さい。(ドキュメントから引用)
必要に応じて認可するアクションごとにメソッドを追加します。
Policyを--modelオプションを付け、Artisanコマンドにより生成した場合、viewAny、view、create、update、delete、restore、forceDeleteアクションが含まれています。

app/Policies/PostPolicy.php
class PostPolicy
{
    public function update(User $user, Post $post)
    {
        return $user->id === $post->user_id;
    }
}

Policyの登録

ポリシーを作成後、AuthServiceProviderのpoliciesプロパティに登録します。

AuthServiceProvider.php
class AuthServiceProvider extends ServiceProvider
{
 /**
   * アプリケーションにマップ付されたポリシー
   *
   * @var array
   */
    protected $policies = [
        'App\Models\Post' => 'App\Policies\PostPolicy',
    ];
}

~注意点~
Laravelでは、モデルとポリシーの標準命名規則にしたがっているポリシーを自動的に検出します。
自動検出が行われる対象となるのは、モデルが含まれているディレクトリ下に存在する、PoliciesディレクトリのPolicyです。
例えば、モデルがappディレクトリ下にあれば、Policyはapp/Policiesディレクトリへ置く必要があります。また、Policyの名前は対応するモデルの名前へ、Policyサフィックスを付けたものにする必要があります。従って、例えばUserモデルに対応させるには、UserPolicyクラスと命名します。

Policyで定義した認可の使用方法

Policyはコントローラーやmiddlewareで、canやcantを呼び出して利用します。

・Userモデルのcanメソッドによるアクセス制限
Laravelアプリケーションに含まれるUserモデルは、アクションを認可するためにcancantというメソッドを持っています。
Controllerクラス内でcanメソッド認可したいアクションと関連するモデルを引数に取ります。

下記では、ユーザーが指定したPostの更新を認可するかを記述しています。
指定するモデル(Post)に対するポリシー(updateメソッド)が登録済みであれば適切なポリシーに対するcanメソッドが自動的に呼びだされ、論理型の結果が返されます。

PostController.php
public function update(Post $post){

    $user = auth()->user(); //アクセスしているユーザ情報を取得

    if($user->can('update',$post)){
        //略
    	return view('posts.show')->with('msg_success', '更新が成功しました');

    }else{
        dd('更新する権限がありません。');
    }
}

``・authorizeメソッドによるアクセス制限`` ``App\Http\Controllers\Controllerベースクラス``を拡張しているコントローラに対し、Laravelは``authorizeメソッド``を提供しています。 canメソッドと同様に、このメソッドは**認可対象のアクション名と関連するモデルを引数に取ります。** アクションが認可されない場合、authorizeメソッドは``Illuminate\Auth\Access\AuthorizationException例外``を投げ、これはデフォルトでLaravelの例外ハンドラにより、403ステータスコードのHTTPレスポンスへ変換されます。
PostController.php
class PostController extends Controller
{
 public function update(Request $request, Post $post)
    {
        $this->authorize('update', $post);
        // 現在のユーザーはブログポストの更新が可能
    }
}

``・ミドルウェアによるアクセス制限`` Laravelには、送信されたリクエストがルートやコントローラへ到達する前に、アクションを認可できるミドルウェアがあります。 デフォルトで``App\Http\Kernelクラスの中でcanキー``に``Illuminate\Auth\Middleware\Authorizeミドルウェア``が割り付けられています。

下記の例はドキュメントから引用しています。

この例では、canミドルウェアへ2つの引数を渡しています。最初の引数は認可したいアクションの名前です。2つ目はポリシーメソッドに渡したいルートパラメータです。この場合、暗黙のモデル結合を使用しているため、Postモデルがポリシーメソッドへ渡されます。ユーザーに指定したアクションを実行する認可がない場合、ミドルウェアは403ステータスコードのHTTPレスポンスを生成します。
Laravelドキュメント 認可

web.php
use App\Post;

Route::put('/post/{post}', function (Post $post) {
    // 現在のユーザーはこのポストを更新できる
})->middleware('can:update,post');

``・リソースコントローラにおけるアクセス制限`` リソースコントローラを利用している場合、コントローラのコンストラクタの中で、``authorizeResourceメソッド``を使用できます。 このメソッドはリソースコントローラのメソッドへ適切なcanミドルウェア定義を付加します。 authorizeResourceメソッドは、**最初の引数にモデルのクラス名**を指定し、第二引数にはモデルのIDを含むルート/リクエストパラメータ名を指定します。
PostController.php
class PostController extends Controller
{
    public function __construct()
    {
        $this->authorizeResource(Post::class, 'post');
    }
}

補足

・GateとPolicyの使い分け

私は、特定のモデルやリソースに関するものはPolicyを利用し、それ以外はGateを利用するようにしています。

Gateは、主に特定のモデルに関連していないユーザのアクションに関してアクセス制限を行う際に使用します。
例えば、管理者画面にアクセスできるユーザを制限する際にGateを利用することができます。

Policyは、特定のモデルやリソースに対して行うアクション(作成、更新、削除、閲覧等)に関してアクセス制限を行う際に使用します。

・詳細なレスポンスを返す

GateやPolicyで単純な論理値を返す代わりに、Response::allow() / Response::deny()を使い、
呼び出し側ではGate::inspect()を呼び出すことで、Policyから認可レスポンスを受け取ることができます。

GateやPolicyメソッドからIlluminate\Auth\Access\Responseを返すように設定して下さい。

Gateの場合

AuthServiceProvider.php
use Illuminate\Auth\Access\Response;
use Illuminate\Support\Facades\Gate;

Gate::define('update', function ($user) {
    return $user->isAdmin
                ? Response::allow()
                : Response::deny('更新の権限が与えられていません');
});
PostController.php
$response = Gate::inspect('update', $post);

if ($response->allowed()) {
    //許可
} else {
    echo $response->message();
}

Policyの場合

app/Policies/PostPolicy.php
use Illuminate\Auth\Access\Response;

public function update(User $user, Post $post)
{
    return $user->id === $post->user_id
                ? Response::allow()
                : Response::deny('更新の権限が与えられていません');
}
PostController.php
$response = Gate::inspect('update', $post);

if ($response->allowed()) {
    //許可
} else {
    echo $response->message();
}

・モデルインスタンスを必要としない認可

createのようにモデルインスタンスを必要としない場合があります。
そのような場合は、モデルインスタンスの代わりに別のものを引数に渡します。

example.php
//Policyメソッドを定義する際のcreateの場合
//モデルインスタンスの代わりに、その認証済みユーザーが期待している人物かをメソッドで定義します
//HTTPリクエストが認証済みユーザーにより開始されたものでなければ、全てのゲートとポリシーは自動的にデフォルトとしてfalseを返します。
class ExamplePolicy
{
public function create(User $user)
 {
    //
 }
}

//Policyを利用したアクションの認可の場合
//各コントローラのメソッド内の、$user->canでPolisyのcreateメソッドを使う場合はクラス名を渡します。
//クラス名はアクションを認可するときにどのポリシーを使用すべきかを決めるために使われます。
use App\Post;

public function create(Request $request)
{
   if ($user->can('create', Post::class)) {
    // 関連するポリシーの"create"メソッドが実行される
 }
}

//コントローラヘルパによる認可の場合
//コントローラーにおけるauthorizeメソッドの場合、上記と同じく第二引数にクラス名を渡します。
public function create(Request $request)
{
    $this->authorize('create', Post::class);
    // 現在のユーザーはブログポストを生成できる
}

//ミドルウェアの場合
//ミドルウェアの第二引数にクラス名を渡します。
//クラス名はアクションを認可するときに、どのポリシーを使用するかの判断に使われます。
Route::post('/post', function () {
    // 現在のユーザーはポストを更新できる
})->middleware('can:create,App\Post');

参考文献

Laravel6.x 公式ドキュメント  認可
Laravel GitHub

1
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?