イントロダクションIntroduction
組み込み認証サービスの提供に加え、Laravelは特定のリソースに対するユーザーアクションを認可する手軽な方法も提供しています。たとえば、あるユーザーが認証されていても、アプリケーションが管理している特定のEloquentモデルまたはデータベースレコードを更新や削除する権限を持っていない場合があるでしょう。Laravelの認可機能は、こうしたタイプの認可チェックを管理するための簡単で組織化された方法を提供します。In addition to providing built-in authentication[/docs/{{version}}/authentication] services, Laravel also provides a simple way to authorize user actions against a given resource. For example, even though a user is authenticated, they may not be authorized to update or delete certain Eloquent models or database records managed by your application. Laravel's authorization features provide an easy, organized way of managing these types of authorization checks.
Laravelは、アクションを認可する2つの主要な方法を提供します。ゲートとポリシーです。ゲートとポリシーは、ルートやコントローラのようなものだと考えてください。ゲートは認可のためのクロージャベースのシンプルなアプローチを提供します。一方でポリシーはコントローラのように、特定のモデルやリソース周辺のロジックをひとかたまりにまとめます。このドキュメントでは、最初にゲートを説明し、その後でポリシーを見ていきましょう。Laravel provides two primary ways of authorizing actions: gates[#gates] and policies[#creating-policies]. Think of gates and policies like routes and controllers. Gates provide a simple, closure-based approach to authorization while policies, like controllers, group logic around a particular model or resource. In this documentation, we'll explore gates first and then examine policies.
アプリケーションを構築するときに、ゲートのみを使用するか、ポリシーのみを使用するかを選択する必要はありません。ほとんどのアプリケーションには、ゲートとポリシーが混在する可能性が高く、それはまったく問題ありません。ゲートは、管理者ダッシュボードの表示など、モデルやリソースに関連しないアクションに最も適しています。対照的に、特定のモデルまたはリソースのアクションを認可する場合は、ポリシーを使用する必要があります。You do not need to choose between exclusively using gates or exclusively using policies when building an application. Most applications will most likely contain some mixture of gates and policies, and that is perfectly fine! Gates are most applicable to actions that are not related to any model or resource, such as viewing an administrator dashboard. In contrast, policies should be used when you wish to authorize an action for a particular model or resource.
ゲートGates
ゲートの作成Writing Gates
ポリシーを使用して認可ルールを整理することを検討する必要があります。[!WARNING]
Warning! ゲートは、Laravelの認可機能の基本を学ぶための優れた方法です。ただし、堅牢なLaravelアプリケーションを構築するときは、
Gates are a great way to learn the basics of Laravel's authorization features; however, when building robust Laravel applications you should consider using policies[#creating-policies] to organize your authorization rules.
ゲートは、指定アクションをユーザーが実行する権限があるかを判定するシンプルなクロージャです。通常、ゲートはGate
ファサードを使用して、App\Providers\AppServiceProvider
クラスのboot
メソッド内で定義します。ゲートは常に最初の引数としてユーザーインスタンスを受け取り、オプションで関連するEloquentモデルなどの追加の引数を受け取ることもできます。Gates are simply closures that determine if a user is authorized to perform a given action. Typically, gates are defined within the boot
method of the App\Providers\AppServiceProvider
class using the Gate
facade. Gates always receive a user instance as their first argument and may optionally receive additional arguments such as a relevant Eloquent model.
以下の例では、ユーザーが特定のApp\Models\Post
モデルを更新できるかどうかを判断するためのゲートを定義します。ユーザーのid
と、投稿を作成したユーザーのuser_id
を比較することで、このゲートは可否を判定します。In this example, we'll define a gate to determine if a user can update a given App\Models\Post
model. The gate will accomplish this by comparing the user's id
against the user_id
of the user that created the post:
use App\Models\Post;
use App\Models\User;
use Illuminate\Support\Facades\Gate;
/**
* アプリケーションの全サービスの初期起動処理
*/
public function boot(): void
{
Gate::define('update-post', function (User $user, Post $post) {
return $user->id === $post->user_id;
});
}
コントローラと同様に、ゲートもクラスコールバック配列を使用して定義できます。Like controllers, gates may also be defined using a class callback array:
use App\Policies\PostPolicy;
use Illuminate\Support\Facades\Gate;
/**
* アプリケーションの全サービスの初期起動処理
*/
public function boot(): void
{
Gate::define('update-post', [PostPolicy::class, 'update']);
}
アクションの認可Authorizing Actions
ゲートを使用してアクションを認可するには、Gate
ファサードが提供するallows
かdenies
メソッドを使用する必要があります。現在認証済みのユーザーをこれらのメソッドに渡す必要はないことに注意してください。Laravelは自動的にユーザーをゲートクロージャに引き渡します。認可が必要なアクションを実行する前に、アプリケーションのコントローラ内でゲート認可メソッドを呼び出すのが一般的です。To authorize an action using gates, you should use the allows
or denies
methods provided by the Gate
facade. Note that you are not required to pass the currently authenticated user to these methods. Laravel will automatically take care of passing the user into the gate closure. It is typical to call the gate authorization methods within your application's controllers before performing an action that requires authorization:
<?php
namespace App\Http\Controllers;
use App\Http\Controllers\Controller;
use App\Models\Post;
use Illuminate\Http\RedirectResponse;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Gate;
class PostController extends Controller
{
/**
* 指定した投稿を更新
*/
public function update(Request $request, Post $post): RedirectResponse
{
if (! Gate::allows('update-post', $post)) {
abort(403);
}
// 投稿を更新…
return redirect('/posts');
}
}
現在認証済みユーザー以外のユーザーがアクションの実行を許可されているかを確認する場合は、Gate
ファサードでforUser
メソッドを使用します。If you would like to determine if a user other than the currently authenticated user is authorized to perform an action, you may use the forUser
method on the Gate
facade:
if (Gate::forUser($user)->allows('update-post', $post)) {
// ユーザーは投稿を更新可能
}
if (Gate::forUser($user)->denies('update-post', $post)) {
// ユーザーは投稿を更新不可能
}
any
またはnone
メソッドを使用して、一度に複数のアクション認可を確認できます。You may authorize multiple actions at a time using the any
or none
methods:
if (Gate::any(['update-post', 'delete-post'], $post)) {
// ユーザーは投稿を更新または削除可能
}
if (Gate::none(['update-post', 'delete-post'], $post)) {
// ユーザーは投稿を更新または削除不可能
}
認可または例外を投げるAuthorizing or Throwing Exceptions
アクションの認可を試みて、ユーザーが指定アクションを実行できない場合、自動的にIlluminate\Auth\Access\AuthorizationException
を投げたい場合、Gate
ファサードのauthorize
メソッドを使用します。AuthorizationException
のインスタンスは、Laravelによって自動的に403 HTTPレスポンスに変換されます。If you would like to attempt to authorize an action and automatically throw an Illuminate\Auth\Access\AuthorizationException
if the user is not allowed to perform the given action, you may use the Gate
facade's authorize
method. Instances of AuthorizationException
are automatically converted to a 403 HTTP response by Laravel:
Gate::authorize('update-post', $post);
// アクションは認可されている
追加コンテキストの提供Supplying Additional Context
アビリティを認可するためのゲートメソッド(allows
、denis
、check
、any
、none
、authorize
、can
、cannot
)と認可Bladeディレクティブ(@can
、@cannot
、@canany
)は、2番目の引数として配列を取れます。これらの配列要素は、パラメータとしてゲートクロージャに渡され、認可を決定する際の追加のコンテキストに使用できます。The gate methods for authorizing abilities (allows
, denies
, check
, any
, none
, authorize
, can
, cannot
) and the authorization Blade directives[#via-blade-templates] (@can
, @cannot
, @canany
) can receive an array as their second argument. These array elements are passed as parameters to the gate closure, and can be used for additional context when making authorization decisions:
use App\Models\Category;
use App\Models\User;
use Illuminate\Support\Facades\Gate;
Gate::define('create-post', function (User $user, Category $category, bool $pinned) {
if (! $user->canPublishToGroup($category->group)) {
return false;
} elseif ($pinned && ! $user->canPinPosts()) {
return false;
}
return true;
});
if (Gate::check('create-post', [$category, $pinned])) {
// ユーザーは投稿を作成可能
}
ゲートのレスポンスGate Responses
これまで、単純な論理値を返すゲートのみ見てきました。しかし、エラーメッセージなどのより詳細なレスポンスを返したい場合もあります。これには、ゲートからIlluminate\Auth\Access\Response
を返してください。So far, we have only examined gates that return simple boolean values. However, sometimes you may wish to return a more detailed response, including an error message. To do so, you may return an Illuminate\Auth\Access\Response
from your gate:
use App\Models\User;
use Illuminate\Auth\Access\Response;
use Illuminate\Support\Facades\Gate;
Gate::define('edit-settings', function (User $user) {
return $user->isAdmin
? Response::allow()
: Response::deny('You must be an administrator.');
});
ゲートから認可レスポンスを返した場合でも、Gate::allows
メソッドは単純なブール値を返します。ただし、Gate::inspect
メソッドを使用して、ゲートから返される完全な認可レスポンスを取得できます。Even when you return an authorization response from your gate, the Gate::allows
method will still return a simple boolean value; however, you may use the Gate::inspect
method to get the full authorization response returned by the gate:
$response = Gate::inspect('edit-settings');
if ($response->allowed()) {
// アクションは認可されている
} else {
echo $response->message();
}
アクションが認可されていない場合にAuthorizationException
を投げるGate::authorize
メソッドを使用すると、認可レスポンスが提供するエラーメッセージがHTTPレスポンスへ伝播されます。When using the Gate::authorize
method, which throws an AuthorizationException
if the action is not authorized, the error message provided by the authorization response will be propagated to the HTTP response:
Gate::authorize('edit-settings');
// アクションは認可されている
HTTPレスポンスステータスのカスタマイズCustomizing The HTTP Response Status
ゲートがアクションを拒否すると、403
HTTPレスポンスを返します。しかし場合により、別のHTTPステータスコードを返すほうが、便利なことがあります。認可チェックに失敗したときに返すHTTPステータスコードは、Illuminate\Auth\Access\Response
クラスのdenyWithStatus
静的コンストラクタを使用してカスタマイズできます。When an action is denied via a Gate, a 403
HTTP response is returned; however, it can sometimes be useful to return an alternative HTTP status code. You may customize the HTTP status code returned for a failed authorization check using the denyWithStatus
static constructor on the Illuminate\Auth\Access\Response
class:
use App\Models\User;
use Illuminate\Auth\Access\Response;
use Illuminate\Support\Facades\Gate;
Gate::define('edit-settings', function (User $user) {
return $user->isAdmin
? Response::allow()
: Response::denyWithStatus(404);
});
404
レスポンスによるリソースの隠蔽はウェブアプリケーションでは常套手段なため、使いやすいようにdenyAsNotFound
メソッドを提供しています。Because hiding resources via a 404
response is such a common pattern for web applications, the denyAsNotFound
method is offered for convenience:
use App\Models\User;
use Illuminate\Auth\Access\Response;
use Illuminate\Support\Facades\Gate;
Gate::define('edit-settings', function (User $user) {
return $user->isAdmin
? Response::allow()
: Response::denyAsNotFound();
});
ゲートチェックの割り込みIntercepting Gate Checks
特定のユーザーにすべての機能を付与したい場合があります。before
メソッドを使用して、他のすべての認可チェックの前に実行するクロージャを定義できます。Sometimes, you may wish to grant all abilities to a specific user. You may use the before
method to define a closure that is run before all other authorization checks:
use App\Models\User;
use Illuminate\Support\Facades\Gate;
Gate::before(function (User $user, string $ability) {
if ($user->isAdministrator()) {
return true;
}
});
before
クロージャがnull以外の結果を返した場合、その結果を認可チェックの結果とみなします。If the before
closure returns a non-null result that result will be considered the result of the authorization check.
after
メソッドを使用して、他のすべての認可チェックの後に実行されるクロージャを定義できます。You may use the after
method to define a closure to be executed after all other authorization checks:
use App\Models\User;
Gate::after(function (User $user, string $ability, bool|null $result, mixed $arguments) {
if ($user->isAdministrator()) {
return true;
}
});
after
クロージャが返す値は、ゲートまたはポリシーがnull
を返さない限り、認可チェックの結果を上書きしません。Values returned by after
closures will not override the result of the authorization check unless the gate or policy returned null
.
インライン認可Inline Authorization
時には、現在認証されているユーザーが、あるアクションを実行する認可を持っているかを、そのアクションに対応する専用のゲートを書かずに判断したいこともあるでしょう。Laravelでは、Gate::allowIf
やGate::denyIf
メソッドを使い、「インライン」での認可チェックを行うことができます。インライン認可は、定義した"before"と"after"の認可フックを実行しません。Occasionally, you may wish to determine if the currently authenticated user is authorized to perform a given action without writing a dedicated gate that corresponds to the action. Laravel allows you to perform these types of "inline" authorization checks via the Gate::allowIf
and Gate::denyIf
methods. Inline authorization does not execute any defined "before" or "after" authorization hooks[#intercepting-gate-checks]:
use App\Models\User;
use Illuminate\Support\Facades\Gate;
Gate::allowIf(fn (User $user) => $user->isAdministrator());
Gate::denyIf(fn (User $user) => $user->banned());
アクションが認可されていない場合や、現在認証されているユーザーがいない場合、Laravelは自動的にIlluminate\Auth\Access\AuthorizationException
という例外を投げます。AuthorizationException
のインスタンスは、Laravelの例外ハンドラが、自動的に403 HTTPレスポンスへ変換します。If the action is not authorized or if no user is currently authenticated, Laravel will automatically throw an Illuminate\Auth\Access\AuthorizationException
exception. Instances of AuthorizationException
are automatically converted to a 403 HTTP response by Laravel's exception handler.
ポリシーの作成Creating Policies
ポリシーの生成Generating Policies
ポリシーは、特定のモデルまたはリソースに関する認可ロジックを集めたクラスです。たとえば、アプリケーションがブログの場合、App\Models\Post
モデルと投稿の作成や更新などのユーザーアクションを認可するためのPostモデルと対応するApp\Policies\PostPolicy
があるでしょう。Policies are classes that organize authorization logic around a particular model or resource. For example, if your application is a blog, you may have an App\Models\Post
model and a corresponding App\Policies\PostPolicy
to authorize user actions such as creating or updating posts.
make:policy
Artisanコマンドを使用してポリシーを生成できます。生成するポリシーはapp/Policies
ディレクトリへ配置します。このディレクトリがアプリケーションに存在しない場合、Laravelが作成します。You may generate a policy using the make:policy
Artisan command. The generated policy will be placed in the app/Policies
directory. If this directory does not exist in your application, Laravel will create it for you:
php artisan make:policy PostPolicy
make:policy
コマンドは、空のポリシークラスを生成します。リソースの表示、作成、更新、削除に関連するポリシーメソッドのサンプルを含んだクラスを生成する場合は、コマンドの実行時に--model
オプションを指定します。The make:policy
command will generate an empty policy class. If you would like to generate a class with example policy methods related to viewing, creating, updating, and deleting the resource, you may provide a --model
option when executing the command:
php artisan make:policy PostPolicy --model=Post
ポリシーの登録Registering Policies
ポリシーの検出Policy Discovery
デフォルトでは、モデルとポリシーが標準的なLaravelの命名規則に従っている限り、Laravelは自動的にポリシーを検出します。具体的にはモデルを含むディレクトリの上、またはそのディレクトリの中のPolicies
ディレクトリへ、ポリシーを置く必要があります。例えば、モデルは app/Models
ディレクトリに配置し、ポリシーは app/Policies
ディレクトリに配置します。この場合、Laravelはapp/Models/Policies
にあるポリシーをチェックし、次にapp/Policies
にあるポリシーをチェックします。さらに、ポリシー名はモデル名と一致し、最後にPolicy
を付ける必要があります。つまり、User
モデルは、UserPolicy
ポリシークラスに対応します。By default, Laravel automatically discover policies as long as the model and policy follow standard Laravel naming conventions. Specifically, the policies must be in a Policies
directory at or above the directory that contains your models. So, for example, the models may be placed in the app/Models
directory while the policies may be placed in the app/Policies
directory. In this situation, Laravel will check for policies in app/Models/Policies
then app/Policies
. In addition, the policy name must match the model name and have a Policy
suffix. So, a User
model would correspond to a UserPolicy
policy class.
独自のポリシー検出ロジックを定義したい場合は、Gate::guessPolicyNamesUsing
メソッドを使用し、カスタムポリシー検出コールバックを登録してください。通常、このメソッドはアプリケーションのAppServiceProvider
のboot
メソッドから呼び出します。If you would like to define your own policy discovery logic, you may register a custom policy discovery callback using the Gate::guessPolicyNamesUsing
method. Typically, this method should be called from the boot
method of your application's AppServiceProvider
:
use Illuminate\Support\Facades\Gate;
Gate::guessPolicyNamesUsing(function (string $modelClass) {
// 指定されたモデルに対するポリシークラスの名前を返す…
});
ポリシーの手作業による登録Manually Registering Policies
Gate
ファサードを使用すると、アプリケーションの AppServiceProvider
のboot
メソッド内で、ポリシーと対応するモデルを手作業で登録できます。Using the Gate
facade, you may manually register policies and their corresponding models within the boot
method of your application's AppServiceProvider
:
use App\Models\Order;
use App\Policies\OrderPolicy;
use Illuminate\Support\Facades\Gate;
/**
* アプリケーションの全サービスの初期起動処理
*/
public function boot(): void
{
Gate::policy(Order::class, OrderPolicy::class);
}
ポリシーの作成Writing Policies
ポリシーメソッドPolicy Methods
ポリシークラスを登録したら、認可するアクションごとにメソッドを追加できます。例として、あるApp\Models\User
があるApp\Models\Post
インスタンスを更新できるかどうかを決定するPostPolicy
でupdate
メソッドを定義してみましょう。Once the policy class has been registered, you may add methods for each action it authorizes. For example, let's define an update
method on our PostPolicy
which determines if a given App\Models\User
can update a given App\Models\Post
instance.
update
メソッドは引数としてUser
とPost
インスタンスを受け取り、そのユーザーが指定したPost
を更新する権限があるかどうかを示すtrue
またはfalse
を返す必要があります。したがって、この例では、ユーザーのid
が投稿のuser_id
と一致することを確認しています。The update
method will receive a User
and a Post
instance as its arguments, and should return true
or false
indicating whether the user is authorized to update the given Post
. So, in this example, we will verify that the user's id
matches the user_id
on the post:
<?php
namespace App\Policies;
use App\Models\Post;
use App\Models\User;
class PostPolicy
{
/**
* 指定した投稿をユーザーが更新可能かを判定
*/
public function update(User $user, Post $post): bool
{
return $user->id === $post->user_id;
}
}
ポリシーが認可するさまざまなアクションの必要に合わせ、ポリシーに追加のメソッドをどんどん定義できます。たとえば、view
またはdelete
メソッドを定義して、さまざまなPost
関連のアクションを認可できますが、ポリシーメソッドには任意の名前を付けることができることを覚えておいてください。You may continue to define additional methods on the policy as needed for the various actions it authorizes. For example, you might define view
or delete
methods to authorize various Post
related actions, but remember you are free to give your policy methods any name you like.
Artisanコンソールを介してポリシーを生成するときに--model
オプションを使用した場合、はじめからviewAny
、view
、create
、update
、delete
、restore
、forceDelete
アクションのメソッドが用意されます。If you used the --model
option when generating your policy via the Artisan console, it will already contain methods for the viewAny
, view
, create
, update
, delete
, restore
, and forceDelete
actions.
サービスコンテナを介して解決されるため、ポリシーのコンストラクターで必要な依存関係をタイプヒントして、自動的に依存注入することができます。[!NOTE]
Note: すべてのポリシーはLaravel
All policies are resolved via the Laravel service container[/docs/{{version}}/container], allowing you to type-hint any needed dependencies in the policy's constructor to have them automatically injected.
ポリシーのレスポンスPolicy Responses
これまで、単純な論理値値を返すポリシーメソッドについてのみ説明してきました。しかし、エラーメッセージなどより詳細なレスポンスを返したい場合があります。これにはポリシーメソッドからIlluminate\Auth\Access\Response
インスタンスを返してください。So far, we have only examined policy methods that return simple boolean values. However, sometimes you may wish to return a more detailed response, including an error message. To do so, you may return an Illuminate\Auth\Access\Response
instance from your policy method:
use App\Models\Post;
use App\Models\User;
use Illuminate\Auth\Access\Response;
/**
* 指定された投稿をユーザーが更新可能か判定
*/
public function update(User $user, Post $post): Response
{
return $user->id === $post->user_id
? Response::allow()
: Response::deny('You do not own this post.');
}
ポリシーから認可レスポンスを返す場合でも、Gate::allows
メソッドは単純な論理値を返します。ただし、Gate::inspect
メソッドを使用して、ゲートが返す完全な認可レスポンスを取得できます。When returning an authorization response from your policy, the Gate::allows
method will still return a simple boolean value; however, you may use the Gate::inspect
method to get the full authorization response returned by the gate:
use Illuminate\Support\Facades\Gate;
$response = Gate::inspect('update', $post);
if ($response->allowed()) {
// アクションは認可されている
} else {
echo $response->message();
}
アクションが認可されていない場合にAuthorizationException
を投げるGate::authorize
メソッドを使用すると、認可レスポンスが提供するエラーメッセージがHTTPレスポンスへ伝播されます。When using the Gate::authorize
method, which throws an AuthorizationException
if the action is not authorized, the error message provided by the authorization response will be propagated to the HTTP response:
Gate::authorize('update', $post);
// アクションは認可されている
HTTPレスポンスステータスのカスタマイズCustomizing the HTTP Response Status
ポリシーメソッドがアクションを拒否すると、403
HTTPレスポンスを返します。しかし場合により、別のHTTPステータスコードを返すほうが、便利なことがあります。認可チェックに失敗したときに返すHTTPステータスコードは、Illuminate\Auth\Access\Response
クラスのdenyWithStatus
静的コンストラクタを使用してカスタマイズできます。When an action is denied via a policy method, a 403
HTTP response is returned; however, it can sometimes be useful to return an alternative HTTP status code. You may customize the HTTP status code returned for a failed authorization check using the denyWithStatus
static constructor on the Illuminate\Auth\Access\Response
class:
use App\Models\Post;
use App\Models\User;
use Illuminate\Auth\Access\Response;
/**
* 指定された投稿をユーザーが更新可能か判定
*/
public function update(User $user, Post $post): Response
{
return $user->id === $post->user_id
? Response::allow()
: Response::denyWithStatus(404);
}
404
レスポンスによるリソースの隠蔽はウェブアプリケーションでは常套手段なため、使いやすいようにdenyAsNotFound
メソッドを提供しています。Because hiding resources via a 404
response is such a common pattern for web applications, the denyAsNotFound
method is offered for convenience:
use App\Models\Post;
use App\Models\User;
use Illuminate\Auth\Access\Response;
/**
* 指定された投稿をユーザーが更新可能か判定
*/
public function update(User $user, Post $post): Response
{
return $user->id === $post->user_id
? Response::allow()
: Response::denyAsNotFound();
}
モデルのないメソッドMethods Without Models
一部のポリシーメソッドは、現在認証済みユーザーのインスタンスのみを受け取ります。この状況は、create
アクションを認可するばあいに頻繁に見かけます。たとえば、ブログを作成している場合、ユーザーが投稿の作成を認可されているかを確認したい場合があります。このような状況では、ポリシーメソッドはユーザーインスタンスのみを受け取る必要があります。Some policy methods only receive an instance of the currently authenticated user. This situation is most common when authorizing create
actions. For example, if you are creating a blog, you may wish to determine if a user is authorized to create any posts at all. In these situations, your policy method should only expect to receive a user instance:
/**
* 指定ユーザーが投稿を作成可能か確認
*/
public function create(User $user): bool
{
return $user->role == 'writer';
}
ゲストユーザーGuest Users
デフォルトでは、受信HTTPリクエストが認証済みユーザーによって開始されたものでない場合、すべてのゲートとポリシーは自動的にfalse
を返します。ただし、「オプションの」タイプヒントを宣言するか、ユーザーの引数定義でnull
のデフォルト値を指定することで、これらの認可チェックをゲートとポリシーに渡すことができます。By default, all gates and policies automatically return false
if the incoming HTTP request was not initiated by an authenticated user. However, you may allow these authorization checks to pass through to your gates and policies by declaring an "optional" type-hint or supplying a null
default value for the user argument definition:
<?php
namespace App\Policies;
use App\Models\Post;
use App\Models\User;
class PostPolicy
{
/**
* 指定した投稿をユーザーが更新可能かを判定
*/
public function update(?User $user, Post $post): bool
{
return $user?->id === $post->user_id;
}
}
ポリシーフィルタPolicy Filters
ある特定のユーザーには、特定のポリシー内のすべてのアクションを認可したい場合があります。これには、ポリシーで「before」メソッドを定義します。before
メソッドは、ポリシー上の他のメソッドの前に実行されるため、目的のポリシーメソッドが実際に呼び出される前にアクションを認可する機会に利用できます。この機能は、アプリケーション管理者にアクションの実行を許可するために最も一般的に使用されます。For certain users, you may wish to authorize all actions within a given policy. To accomplish this, define a before
method on the policy. The before
method will be executed before any other methods on the policy, giving you an opportunity to authorize the action before the intended policy method is actually called. This feature is most commonly used for authorizing application administrators to perform any action:
use App\Models\User;
/**
* 事前認可チェックの実行
*/
public function before(User $user, string $ability): bool|null
{
if ($user->isAdministrator()) {
return true;
}
return null;
}
特定のタイプのユーザー全員の認可チェックを拒否したい場合は、before
メソッドからfalse
を返してください。null
を返す場合は、認可チェックはポリシーメソッドへ委ねられます。If you would like to deny all authorization checks for a particular type of user then you may return false
from the before
method. If null
is returned, the authorization check will fall through to the policy method.
Warning! ポリシークラスの
before
メソッドは、チェックしている機能の名前と一致する名前のメソッドがクラスに含まれていない場合は呼び出されません。[!WARNING]
Thebefore
method of a policy class will not be called if the class doesn't contain a method with a name matching the name of the ability being checked.
ポリシーを使用したアクションの認可Authorizing Actions Using Policies
ユーザーモデル経由Via the User Model
Laravelアプリケーションに含まれているApp\Models\User
モデルには、アクションを認可するための2つの便利なメソッドcan
とcannot
が含まれています。can
メソッドとcannot
メソッドは、認可するアクションの名前と関連するモデルを受け取ります。たとえば、ユーザーが特定のApp\Models\Post
モデルを更新する権限を持っているかどうかを確認しましょう。通常、これはコントローラメソッド内で実行されます。The App\Models\User
model that is included with your Laravel application includes two helpful methods for authorizing actions: can
and cannot
. The can
and cannot
methods receive the name of the action you wish to authorize and the relevant model. For example, let's determine if a user is authorized to update a given App\Models\Post
model. Typically, this will be done within a controller method:
<?php
namespace App\Http\Controllers;
use App\Http\Controllers\Controller;
use App\Models\Post;
use Illuminate\Http\RedirectResponse;
use Illuminate\Http\Request;
class PostController extends Controller
{
/**
* 指定した投稿を更新
*/
public function update(Request $request, Post $post): RedirectResponse
{
if ($request->user()->cannot('update', $post)) {
abort(403);
}
// 投稿を更新…
return redirect('/posts');
}
}
指定したモデルのポリシーが登録されているの場合、can
メソッドは自動的に適切なポリシーを呼び出し、論理値の結果を返します。モデルにポリシーが登録されていない場合、can
メソッドは、指定されたアクション名に一致するクロージャベースのゲートを呼び出そうとします。If a policy is registered[#registering-policies] for the given model, the can
method will automatically call the appropriate policy and return the boolean result. If no policy is registered for the model, the can
method will attempt to call the closure-based Gate matching the given action name.
モデルを必要としないアクションActions That Don't Require Models
一部のアクションは、モデルインスタンスを必要としないcreate
などのポリシーメソッドに対応する場合があることに注意してください。このような状況では、クラス名をcan
メソッドに渡すことができます。クラス名は、アクションを認可するときに使用するポリシーを決定するために使用されます。Remember, some actions may correspond to policy methods like create
that do not require a model instance. In these situations, you may pass a class name to the can
method. The class name will be used to determine which policy to use when authorizing the action:
<?php
namespace App\Http\Controllers;
use App\Http\Controllers\Controller;
use App\Models\Post;
use Illuminate\Http\RedirectResponse;
use Illuminate\Http\Request;
class PostController extends Controller
{
/**
* 投稿を作成
*/
public function store(Request $request): RedirectResponse
{
if ($request->user()->cannot('create', Post::class)) {
abort(403);
}
// 投稿を作成…
return redirect('/posts');
}
}
Gate
ファサード経由Via the Gate
Facade
AppModelsUser
モデルに用意している便利なメソッドに加え、Gate
ファサードのauthorize
メソッドでいつでもアクションを認可できます。In addition to helpful methods provided to the App\Models\User
model, you can always authorize actions via the Gate
facade's authorize
method.
can
メソッドと同様に、このメソッドは、認可するアクションの名前とリレーションモデルを受け入れます。アクションが認可されていない場合、authorize
メソッドはIlluminate\Auth\Access\AuthorizationException
例外を投げ、Laravel例外ハンドラは自動的に403ステータスコードのHTTPレスポンスに変換します。Like the can
method, this method accepts the name of the action you wish to authorize and the relevant model. If the action is not authorized, the authorize
method will throw an Illuminate\Auth\Access\AuthorizationException
exception which the Laravel exception handler will automatically convert to an HTTP response with a 403 status code:
<?php
namespace App\Http\Controllers;
use App\Http\Controllers\Controller;
use App\Models\Post;
use Illuminate\Http\RedirectResponse;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Gate;
class PostController extends Controller
{
/**
* 指定したブログ投稿の更新
*
* @throws \Illuminate\Auth\Access\AuthorizationException
*/
public function update(Request $request, Post $post): RedirectResponse
{
Gate::authorize('update', $post);
// 現在のユーザーはこのブログ投稿を更新可能
return redirect('/posts');
}
}
モデルを必要としないアクションActions That Don't Require Models
すでに説明したように、create
などの一部のポリシーメソッドはモデルインスタンスを必要としません。このような状況では、クラス名をauthorize
メソッドに渡す必要があります。クラス名は、アクションを認可するときに使用するポリシーを決定するために使用されます。As previously discussed, some policy methods like create
do not require a model instance. In these situations, you should pass a class name to the authorize
method. The class name will be used to determine which policy to use when authorizing the action:
use App\Models\Post;
use Illuminate\Http\RedirectResponse;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Gate;
/**
* 新しいブログ投稿の作成
*
* @throws \Illuminate\Auth\Access\AuthorizationException
*/
public function create(Request $request): RedirectResponse
{
Gate::authorize('create', Post::class);
// 現在のユーザーはブログ投稿を作成可能
return redirect('/posts');
}
ミドルウェア経由Via Middleware
Laravelは、リクエストがルートやコントローラに到達する前にアクションを認可するミドルウェアを用意しています。デフォルトでは、can
ミドルウェアエイリアスを使い、Illuminate\Auth\Middleware\Authorize
ミドルウェアをルートへ指定できます。can
ミドルウェアを使用して、ユーザーが投稿を更新するのを許可する例を見てみましょう:Laravel includes a middleware that can authorize actions before the incoming request even reaches your routes or controllers. By default, the Illuminate\Auth\Middleware\Authorize
middleware may be attached to a route using the can
middleware alias[/docs/{{version}}/middleware#middleware-aliases], which is automatically registered by Laravel. Let's explore an example of using the can
middleware to authorize that a user can update a post:
use App\Models\Post;
Route::put('/post/{post}', function (Post $post) {
// 現在のユーザーは投稿を更新可能
})->middleware('can:update,post');
この例では、can
ミドルウェアに2つの引数を渡します。1つ目は認可するアクションの名前であり、2つ目はポリシーメソッドに渡すルートパラメータです。この場合、暗黙のモデルバインディングを使用しているため、App\Models\Post
モデルがポリシーメソッドに渡されます。ユーザーが特定のアクションを実行する権限を持っていない場合、ミドルウェアは403ステータスコードのHTTPレスポンスを返します。In this example, we're passing the can
middleware two arguments. The first is the name of the action we wish to authorize and the second is the route parameter we wish to pass to the policy method. In this case, since we are using implicit model binding[/docs/{{version}}/routing#implicit-binding], an App\Models\Post
model will be passed to the policy method. If the user is not authorized to perform the given action, an HTTP response with a 403 status code will be returned by the middleware.
これは簡単に、can
メソッドを使い、can
ミドルウェアをルートへ指定できます。For convenience, you may also attach the can
middleware to your route using the can
method:
use App\Models\Post;
Route::put('/post/{post}', function (Post $post) {
// 現在のユーザーは投稿を更新可能
})->can('update', 'post');
モデルを必要としないアクションActions That Don't Require Models
繰り返しますが、create
のようないくつかのポリシーメソッドはモデルインスタンスを必要としません。このような状況では、クラス名をミドルウェアに渡すことができます。クラス名は、アクションを認可するときに使用するポリシーを決定するために使用されます。Again, some policy methods like create
do not require a model instance. In these situations, you may pass a class name to the middleware. The class name will be used to determine which policy to use when authorizing the action:
Route::post('/post', function () {
// 現在のユーザーは投稿を作成可能
})->middleware('can:create,App\Models\Post');
ミドルウェア定義の中で、クラス名全体を文字列で指定するのは面倒です。そのため、can
メソッドを使って、can
ミドルウェアをルートへ指定できます。Specifying the entire class name within a string middleware definition can become cumbersome. For that reason, you may choose to attach the can
middleware to your route using the can
method:
use App\Models\Post;
Route::post('/post', function () {
// 現在のユーザーは投稿を作成可能
})->can('create', Post::class);
Bladeテンプレート経由Via Blade Templates
Bladeテンプレートを作成するとき、ユーザーが特定のアクションを実行する許可がある場合にのみ、ページの一部を表示したい場合があります。たとえば、ユーザーが実際に投稿を更新できる場合にのみ、ブログ投稿の更新フォームを表示したい場合があります。この状況では、@can
および@cannot
ディレクティブを使用できます。When writing Blade templates, you may wish to display a portion of the page only if the user is authorized to perform a given action. For example, you may wish to show an update form for a blog post only if the user can actually update the post. In this situation, you may use the @can
and @cannot
directives:
@can('update', $post)
<!-- 現在のユーザーは投稿を更新可能 -->
@elsecan('create', App\Models\Post::class)
<!-- 現在のユーザーは新しい投稿を作成不可能 -->
@else
<!-- ... -->
@endcan
@cannot('update', $post)
<!-- 現在のユーザーは投稿を更新不可能 -->
@elsecannot('create', App\Models\Post::class)
<!-- 現在のユーザーは新しい投稿を作成可能 -->
@endcannot
これらのディレクティブは、@if
と@unless
ステートメントを短く記述するための便利な短縮形です。上記の@can
および@cannot
ステートメントは、以下のステートメントと同等です。These directives are convenient shortcuts for writing @if
and @unless
statements. The @can
and @cannot
statements above are equivalent to the following statements:
@if (Auth::user()->can('update', $post))
<!-- 現在のユーザーは投稿を更新可能 -->
@endif
@unless (Auth::user()->can('update', $post))
<!-- 現在のユーザーは投稿を更新不可能 -->
@endunless
また、ユーザーが複数のアクションの実行を認可されているかを判定することもできます。これには、@canany
ディレクティブを使用します。You may also determine if a user is authorized to perform any action from a given array of actions. To accomplish this, use the @canany
directive:
@canany(['update', 'view', 'delete'], $post)
<!-- 現在のユーザーは、投稿を更新、表示、削除可能 -->
@elsecanany(['create'], \App\Models\Post::class)
<!-- 現在のユーザーは投稿を作成可能 -->
@endcanany
モデルを必要としないアクションActions That Don't Require Models
他のほとんどの認可メソッドと同様に、アクションがモデルインスタンスを必要としない場合は、クラス名を@can
および@cannot
ディレクティブに渡すことができます。Like most of the other authorization methods, you may pass a class name to the @can
and @cannot
directives if the action does not require a model instance:
@can('create', App\Models\Post::class)
<!-- 現在のユーザーは投稿を作成可能 -->
@endcan
@cannot('create', App\Models\Post::class)
<!-- 現在のユーザーは投稿を作成不可能 -->
@endcannot
追加コンテキストの提供Supplying Additional Context
ポリシーを使用してアクションを認可する場合、2番目の引数としてさまざまな認可関数とヘルパに配列を渡すことができます。配列の最初の要素は、呼び出すポリシーを決定するために使用され、残りの配列要素は、パラメータとしてポリシーメソッドに渡され、認可の決定を行う際の追加のコンテキストに使用できます。たとえば、追加の$category
パラメータを含む次のPostPolicy
メソッド定義について考えてみます。When authorizing actions using policies, you may pass an array as the second argument to the various authorization functions and helpers. The first element in the array will be used to determine which policy should be invoked, while the rest of the array elements are passed as parameters to the policy method and can be used for additional context when making authorization decisions. For example, consider the following PostPolicy
method definition which contains an additional $category
parameter:
/**
* 指定された投稿をユーザーが更新可能か判定
*/
public function update(User $user, Post $post, int $category): bool
{
return $user->id === $post->user_id &&
$user->canUpdateCategory($category);
}
認証済みユーザーが特定の投稿を更新できるか判断する場合、次のようにこのポリシーメソッドを呼び出すことができます。When attempting to determine if the authenticated user can update a given post, we can invoke this policy method like so:
/**
* 指定ブログ投稿を更新
*
* @throws \Illuminate\Auth\Access\AuthorizationException
*/
public function update(Request $request, Post $post): RedirectResponse
{
Gate::authorize('update', [$post, $request->category]);
// 現在のユーザーはブログ投稿を更新可能
return redirect('/posts');
}
認可とInertiaAuthorization & Inertia
認可は常にバリデータ上で処理しなくてはなりませんが、アプリケーションのUIを適切にレンダするために、フロントエンドアプリケーションに認可データを提供すると便利なことがあります。Inertiaを使用したフロントエンドへ認可情報を公開するために必要な規約をLaravelは定義してません。Although authorization must always be handled on the server, it can often be convenient to provide your frontend application with authorization data in order to properly render your application's UI. Laravel does not define a required convention for exposing authorization information to an Inertia powered frontend.
しかし、LaravelのInertiaベースのスターターキットを使用する場合、アプリケーションはあらかじめHandleInertiaRequests
ミドルウェアを用意しています。このミドルウェアのshare
メソッド内から、アプリケーション内のすべてのInertiaページへ提供する共有データを返してください。この共有データは、ユーザーの認可情報を定義するための便利な場所として役立ちます。However, if you are using one of Laravel's Inertia-based starter kits[/docs/{{version}}/starter-kits], your application already contains a HandleInertiaRequests
middleware. Within this middleware's share
method, you may return shared data that will be provided to all Inertia pages in your application. This shared data can serve as a convenient location to define authorization information for the user:
<?php
namespace App\Http\Middleware;
use App\Models\Post;
use Illuminate\Http\Request;
use Inertia\Middleware;
class HandleInertiaRequests extends Middleware
{
// ...
/**
* デフォルトとして共有するプロップを定義
*
* @return array<string, mixed>
*/
public function share(Request $request)
{
return [
...parent::share($request),
'auth' => [
'user' => $request->user(),
'permissions' => [
'post' => [
'create' => $request->user()->can('create', Post::class),
],
],
],
];
}
}