Skip to content

권한 부여

소개

Laravel은 내장된 인증 서비스 외에도 주어진 리소스에 대한 사용자 동작 권한을 부여하는 간단한 방법을 제공합니다. 예를 들어, 사용자가 인증되었더라도 애플리케이션에서 관리하는 특정 Eloquent 모델 또는 데이터베이스 레코드를 업데이트하거나 삭제할 권한이 없을 수 있습니다. Laravel의 권한 부여 기능은 이러한 유형의 권한 부여 검사를 관리하는 쉽고 체계적인 방법을 제공합니다.

Laravel은 동작 권한을 부여하는 두 가지 주요 방법을 제공합니다. 게이트정책입니다. 게이트와 정책을 라우트와 컨트롤러처럼 생각하세요. 게이트는 권한 부여에 대한 간단하고 클로저 기반 접근 방식을 제공하는 반면, 정책은 컨트롤러처럼 특정 모델 또는 리소스 주변의 논리를 그룹화합니다. 이 문서에서는 먼저 게이트를 살펴본 다음 정책을 검토합니다.

애플리케이션을 구축할 때 게이트만 사용하거나 정책만 사용하는 것을 선택할 필요는 없습니다. 대부분의 애플리케이션은 게이트와 정책이 혼합되어 있을 가능성이 높으며, 이는 전혀 문제되지 않습니다! 게이트는 관리자 대시보드 보기와 같이 모델 또는 리소스와 관련이 없는 동작에 가장 적합합니다. 반대로 정책은 특정 모델 또는 리소스에 대한 동작 권한을 부여하려는 경우에 사용해야 합니다.

게이트

게이트 작성

exclamation

게이트는 Laravel 권한 부여 기능의 기본 사항을 배우는 좋은 방법입니다. 그러나 강력한 Laravel 애플리케이션을 구축할 때는 정책을 사용하여 권한 부여 규칙을 구성하는 것을 고려해야 합니다.

게이트는 사용자가 주어진 동작을 수행할 권한이 있는지 여부를 결정하는 단순한 클로저입니다. 일반적으로 게이트는 Gate facade를 사용하여 App\Providers\AppServiceProvider 클래스의 boot 메서드 내에 정의됩니다. 게이트는 항상 사용자 인스턴스를 첫 번째 인수로 받고 관련 Eloquent 모델과 같은 추가 인수를 선택적으로 받을 수 있습니다.

이 예에서는 사용자가 주어진 App\Models\Post 모델을 업데이트할 수 있는지 여부를 결정하는 게이트를 정의합니다. 이 게이트는 사용자의 id를 게시물을 만든 사용자의 user_id와 비교하여 이를 수행합니다.

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;
});
}

컨트롤러와 마찬가지로 게이트는 클래스 콜백 배열을 사용하여 정의할 수도 있습니다.

use App\Policies\PostPolicy;
use Illuminate\Support\Facades\Gate;
 
/**
* 모든 애플리케이션 서비스를 부트스트랩합니다.
*/
public function boot(): void
{
Gate::define('update-post', [PostPolicy::class, 'update']);
}

게이트를 통한 동작 권한 부여

게이트를 사용하여 동작 권한을 부여하려면 Gate facade에서 제공하는 allows 또는 denies 메서드를 사용해야 합니다. 현재 인증된 사용자를 이러한 메서드에 전달할 필요는 없습니다. Laravel은 사용자를 게이트 클로저에 전달하는 작업을 자동으로 처리합니다. 일반적으로 권한 부여가 필요한 동작을 수행하기 전에 애플리케이션의 컨트롤러 내에서 게이트 권한 부여 메서드를 호출합니다.

<?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 facade에서 forUser 메서드를 사용할 수 있습니다.

if (Gate::forUser($user)->allows('update-post', $post)) {
// 사용자는 게시물을 업데이트할 수 있습니다...
}
 
if (Gate::forUser($user)->denies('update-post', $post)) {
// 사용자는 게시물을 업데이트할 수 없습니다...
}

any 또는 none 메서드를 사용하여 한 번에 여러 동작을 권한 부여할 수 있습니다.

if (Gate::any(['update-post', 'delete-post'], $post)) {
// 사용자는 게시물을 업데이트하거나 삭제할 수 있습니다...
}
 
if (Gate::none(['update-post', 'delete-post'], $post)) {
// 사용자는 게시물을 업데이트하거나 삭제할 수 없습니다...
}

예외 권한 부여 또는 발생

동작에 대한 권한을 부여하려고 시도하고 사용자가 주어진 동작을 수행할 수 없는 경우 자동으로 Illuminate\Auth\Access\AuthorizationException을 발생시키려면 Gate facade의 authorize 메서드를 사용할 수 있습니다. AuthorizationException 인스턴스는 Laravel에서 자동으로 403 HTTP 응답으로 변환됩니다.

Gate::authorize('update-post', $post);
 
// 동작이 권한 부여되었습니다...

추가 컨텍스트 제공

권한을 부여하는 게이트 메서드(allows, denies, check, any, none, authorize, can, cannot) 및 권한 부여 Blade 지시어 (@can, @cannot, @canany)는 두 번째 인수로 배열을 받을 수 있습니다. 이러한 배열 요소는 게이트 클로저에 매개변수로 전달되며, 권한 부여 결정을 내릴 때 추가 컨텍스트에 사용할 수 있습니다.

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])) {
// 사용자는 게시물을 만들 수 있습니다...
}

게이트 응답

지금까지 단순한 부울 값을 반환하는 게이트만 검토했습니다. 그러나 때로는 오류 메시지를 포함하여 더 자세한 응답을 반환하고 싶을 수 있습니다. 그렇게 하려면 게이트에서 Illuminate\Auth\Access\Response를 반환할 수 있습니다.

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('관리자여야 합니다.');
});

게이트에서 권한 부여 응답을 반환하더라도 Gate::allows 메서드는 여전히 단순한 부울 값을 반환합니다. 그러나 Gate::inspect 메서드를 사용하여 게이트에서 반환된 전체 권한 부여 응답을 얻을 수 있습니다.

$response = Gate::inspect('edit-settings');
 
if ($response->allowed()) {
// 동작이 권한 부여되었습니다...
} else {
echo $response->message();
}

동작에 대한 권한이 부여되지 않은 경우 AuthorizationException을 발생시키는 Gate::authorize 메서드를 사용할 때 권한 부여 응답에서 제공하는 오류 메시지가 HTTP 응답으로 전파됩니다.

Gate::authorize('edit-settings');
 
// 동작이 권한 부여되었습니다...

HTTP 응답 상태 사용자 지정

게이트를 통해 동작이 거부되면 403 HTTP 응답이 반환됩니다. 그러나 때로는 대체 HTTP 상태 코드를 반환하는 것이 유용할 수 있습니다. Illuminate\Auth\Access\Response 클래스에서 denyWithStatus 정적 생성자를 사용하여 실패한 권한 부여 검사에 대해 반환되는 HTTP 상태 코드를 사용자 지정할 수 있습니다.

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 메서드가 제공됩니다.

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();
});

게이트 검사 가로채기

때로는 특정 사용자에게 모든 기능을 부여하고 싶을 수 있습니다. before 메서드를 사용하여 다른 모든 권한 부여 검사 전에 실행되는 클로저를 정의할 수 있습니다.

use App\Models\User;
use Illuminate\Support\Facades\Gate;
 
Gate::before(function (User $user, string $ability) {
if ($user->isAdministrator()) {
return true;
}
});

before 클로저가 null이 아닌 결과를 반환하면 해당 결과가 권한 부여 검사 결과로 간주됩니다.

after 메서드를 사용하여 다른 모든 권한 부여 검사 후에 실행될 클로저를 정의할 수 있습니다.

use App\Models\User;
 
Gate::after(function (User $user, string $ability, bool|null $result, mixed $arguments) {
if ($user->isAdministrator()) {
return true;
}
});

게이트 또는 정책에서 null을 반환하지 않는 한 after 클로저에서 반환된 값은 권한 부여 검사 결과를 재정의하지 않습니다.

인라인 권한 부여

때로는 동작에 해당하는 전용 게이트를 작성하지 않고 현재 인증된 사용자가 주어진 동작을 수행할 권한이 있는지 확인하고 싶을 수 있습니다. Laravel을 사용하면 Gate::allowIfGate::denyIf 메서드를 통해 이러한 유형의 "인라인" 권한 부여 검사를 수행할 수 있습니다. 인라인 권한 부여는 정의된 "before" 또는 "after" 권한 부여 후크를 실행하지 않습니다.

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 응답으로 변환됩니다.

정책 생성하기

정책 생성하기

정책은 특정 모델 또는 리소스에 대한 권한 부여 로직을 구성하는 클래스입니다. 예를 들어, 애플리케이션이 블로그라면 App\Models\Post 모델과 그에 해당하는 App\Policies\PostPolicy를 사용하여 게시물 생성 또는 업데이트와 같은 사용자 액션을 권한 부여할 수 있습니다.

make:policy Artisan 명령어를 사용하여 정책을 생성할 수 있습니다. 생성된 정책은 app/Policies 디렉토리에 위치합니다. 이 디렉토리가 애플리케이션에 존재하지 않으면 Laravel이 자동으로 생성합니다:

php artisan make:policy PostPolicy

make:policy 명령어는 빈 정책 클래스를 생성합니다. 리소스 보기, 생성, 업데이트, 삭제와 관련된 예시 정책 메서드를 포함하는 클래스를 생성하고 싶다면, 명령어를 실행할 때 --model 옵션을 제공할 수 있습니다:

php artisan make:policy PostPolicy --model=Post

정책 등록

정책 검색

기본적으로 Laravel은 모델과 정책이 표준 Laravel 명명 규칙을 따르는 한 자동으로 정책을 검색합니다. 특히, 정책은 모델이 있는 디렉토리 또는 상위 디렉토리의 Policies 디렉토리에 있어야 합니다. 예를 들어, 모델은 app/Models 디렉토리에 있고 정책은 app/Policies 디렉토리에 있을 수 있습니다. 이 경우 Laravel은 app/Models/Policiesapp/Policies에서 정책을 확인합니다. 또한 정책 이름은 모델 이름과 일치해야 하며 Policy 접미사를 가져야 합니다. 따라서 User 모델은 UserPolicy 정책 클래스에 해당합니다.

자신만의 정책 검색 로직을 정의하고 싶다면 Gate::guessPolicyNamesUsing 메서드를 사용하여 사용자 지정 정책 검색 콜백을 등록할 수 있습니다. 일반적으로 이 메서드는 애플리케이션의 AppServiceProviderboot 메서드에서 호출해야 합니다.

use Illuminate\Support\Facades\Gate;
 
Gate::guessPolicyNamesUsing(function (string $modelClass) {
// 주어진 모델에 대한 정책 클래스 이름을 반환합니다...
});

수동으로 정책 등록

Gate 파사드를 사용하여 애플리케이션의 AppServiceProviderboot 메서드 내에서 정책과 해당 모델을 수동으로 등록할 수 있습니다.

use App\Models\Order;
use App\Policies\OrderPolicy;
use Illuminate\Support\Facades\Gate;
 
/**
* 모든 애플리케이션 서비스 부트스트랩.
*/
public function boot(): void
{
Gate::policy(Order::class, OrderPolicy::class);
}

정책 작성

정책 메서드

정책 클래스가 등록되면 승인하는 각 동작에 대한 메서드를 추가할 수 있습니다. 예를 들어, 주어진 App\Models\User가 주어진 App\Models\Post 인스턴스를 업데이트할 수 있는지 여부를 결정하는 PostPolicyupdate 메서드를 정의해 보겠습니다.

update 메서드는 UserPost 인스턴스를 인수로 받고, 사용자가 주어진 Post를 업데이트할 권한이 있는지 여부를 나타내는 true 또는 false를 반환해야 합니다. 따라서 이 예에서는 사용자의 id가 게시물의 user_id와 일치하는지 확인합니다.

<?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;
}
}

정책이 승인하는 다양한 동작에 필요한 경우 정책에 추가 메서드를 계속 정의할 수 있습니다. 예를 들어, 다양한 Post 관련 동작을 승인하기 위해 view 또는 delete 메서드를 정의할 수 있지만 정책 메서드에 원하는 이름을 자유롭게 지정할 수 있습니다.

Artisan 콘솔을 통해 정책을 생성할 때 --model 옵션을 사용한 경우, viewAny, view, create, update, delete, restoreforceDelete 동작에 대한 메서드가 이미 포함되어 있습니다.

lightbulb

모든 정책은 Laravel 서비스 컨테이너를 통해 해결되므로 정책의 생성자에 필요한 종속성을 타입 힌트하여 자동으로 주입할 수 있습니다.

정책 응답

지금까지는 단순한 부울 값을 반환하는 정책 메서드만 살펴보았습니다. 그러나 때로는 오류 메시지를 포함하여 더 자세한 응답을 반환하고 싶을 수도 있습니다. 그렇게 하려면 정책 메서드에서 Illuminate\Auth\Access\Response 인스턴스를 반환할 수 있습니다.

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('이 게시물의 소유자가 아닙니다.');
}

정책에서 권한 부여 응답을 반환할 때 Gate::allows 메서드는 여전히 단순한 부울 값을 반환합니다. 그러나 Gate::inspect 메서드를 사용하여 게이트에서 반환된 전체 권한 부여 응답을 가져올 수 있습니다.

use Illuminate\Support\Facades\Gate;
 
$response = Gate::inspect('update', $post);
 
if ($response->allowed()) {
// 동작이 승인되었습니다...
} else {
echo $response->message();
}

작업이 승인되지 않은 경우 AuthorizationException을 발생시키는 Gate::authorize 메서드를 사용할 때, 권한 부여 응답에서 제공된 오류 메시지가 HTTP 응답으로 전파됩니다.

Gate::authorize('update', $post);
 
// 동작이 승인되었습니다...

HTTP 응답 상태 사용자 정의

정책 메서드를 통해 작업이 거부되면 403 HTTP 응답이 반환됩니다. 그러나 때로는 대체 HTTP 상태 코드를 반환하는 것이 유용할 수 있습니다. Illuminate\Auth\Access\Response 클래스에서 denyWithStatus 정적 생성자를 사용하여 실패한 권한 부여 검사에 대해 반환되는 HTTP 상태 코드를 사용자 정의할 수 있습니다.

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 메서드가 제공됩니다.

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();
}

모델 없는 메서드

일부 정책 메서드는 현재 인증된 사용자의 인스턴스만 받습니다. 이러한 상황은 create 작업을 승인할 때 가장 일반적입니다. 예를 들어, 블로그를 만들고 있는 경우 사용자가 게시물을 만들 권한이 있는지 여부를 확인하고 싶을 수 있습니다. 이러한 경우 정책 메서드는 사용자 인스턴스만 수신해야 합니다.

/**
* 주어진 사용자가 게시물을 만들 수 있는지 여부를 결정합니다.
*/
public function create(User $user): bool
{
return $user->role == 'writer';
}

게스트 사용자

기본적으로 들어오는 HTTP 요청이 인증된 사용자에 의해 시작되지 않은 경우 모든 게이트와 정책은 자동으로 false를 반환합니다. 그러나 이러한 권한 부여 검사가 "선택적" 유형 힌트를 선언하거나 사용자 인수 정의에 null 기본값을 제공하여 게이트 및 정책으로 전달되도록 할 수 있습니다.

<?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;
}
}

정책 필터

특정 사용자의 경우 주어진 정책 내에서 모든 작업을 승인하고 싶을 수 있습니다. 이를 위해 정책에 before 메서드를 정의합니다. before 메서드는 정책의 다른 모든 메서드 전에 실행되어 의도된 정책 메서드가 실제로 호출되기 전에 동작을 승인할 수 있는 기회를 제공합니다. 이 기능은 일반적으로 애플리케이션 관리자가 모든 작업을 수행할 수 있도록 권한을 부여하는 데 사용됩니다.

use App\Models\User;
 
/**
* 사전 권한 부여 검사를 수행합니다.
*/
public function before(User $user, string $ability): bool|null
{
if ($user->isAdministrator()) {
return true;
}
 
return null;
}

특정 유형의 사용자에 대한 모든 권한 부여 검사를 거부하려면 before 메서드에서 false를 반환할 수 있습니다. null이 반환되면 권한 부여 검사는 정책 메서드로 넘어갑니다.

exclamation

정책 클래스의 before 메서드는 클래스에 확인 중인 능력 이름과 일치하는 이름의 메서드가 포함되어 있지 않으면 호출되지 않습니다.

정책을 사용하여 작업 승인

사용자 모델을 통해

Laravel 애플리케이션에 포함된 App\Models\User 모델에는 작업을 승인하기 위한 두 가지 유용한 메서드인 cancannot이 포함되어 있습니다. cancannot 메서드는 승인하려는 동작의 이름과 관련 모델을 받습니다. 예를 들어, 사용자가 주어진 App\Models\Post 모델을 업데이트할 권한이 있는지 여부를 결정해 보겠습니다. 일반적으로 이것은 컨트롤러 메서드 내에서 수행됩니다.

<?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 메서드는 주어진 동작 이름과 일치하는 클로저 기반 게이트를 호출하려고 시도합니다.

모델이 필요 없는 사용자 모델 작업

일부 작업은 모델 인스턴스가 필요하지 않은 create와 같은 정책 메서드에 해당할 수 있습니다. 이러한 경우 can 메서드에 클래스 이름을 전달할 수 있습니다. 클래스 이름은 작업을 승인할 때 사용할 정책을 결정하는 데 사용됩니다.

<?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 파사드를 통해

App\Models\User 모델에 제공된 유용한 메서드 외에도 Gate 파사드의 authorize 메서드를 통해 항상 작업을 승인할 수 있습니다.

can 메서드와 마찬가지로, 이 메서드는 승인하려는 동작의 이름과 관련 모델을 받습니다. 작업이 승인되지 않은 경우, authorize 메서드는 Illuminate\Auth\Access\AuthorizationException 예외를 발생시키며, Laravel 예외 처리기는 자동으로 이 예외를 403 상태 코드가 있는 HTTP 응답으로 변환합니다.

<?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');
}
}

모델이 필요 없는 컨트롤러 작업

앞서 논의한 바와 같이, create와 같은 일부 정책 메서드는 모델 인스턴스가 필요하지 않습니다. 이러한 경우, authorize 메서드에 클래스 이름을 전달해야 합니다. 클래스 이름은 작업을 승인할 때 사용할 정책을 결정하는 데 사용됩니다.

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');
}

미들웨어를 통해

Laravel에는 들어오는 요청이 라우트 또는 컨트롤러에 도달하기 전에 작업을 승인할 수 있는 미들웨어가 포함되어 있습니다. 기본적으로 Illuminate\Auth\Middleware\Authorize 미들웨어는 Laravel에서 자동으로 등록된 can 미들웨어 별칭을 사용하여 라우트에 연결할 수 있습니다. 사용자가 게시물을 업데이트할 수 있도록 권한을 부여하기 위해 can 미들웨어를 사용하는 예제를 살펴보겠습니다.

use App\Models\Post;
 
Route::put('/post/{post}', function (Post $post) {
// 현재 사용자는 게시물을 업데이트할 수 있습니다...
})->middleware('can:update,post');

이 예제에서는 can 미들웨어에 두 개의 인수를 전달합니다. 첫 번째는 승인하려는 동작의 이름이고 두 번째는 정책 메서드에 전달하려는 라우트 매개변수입니다. 이 경우 암시적 모델 바인딩을 사용하고 있으므로 App\Models\Post 모델이 정책 메서드에 전달됩니다. 사용자가 주어진 작업을 수행할 권한이 없는 경우, 미들웨어에서 403 상태 코드가 있는 HTTP 응답이 반환됩니다.

편의를 위해 can 메서드를 사용하여 라우트에 can 미들웨어를 연결할 수도 있습니다.

use App\Models\Post;
 
Route::put('/post/{post}', function (Post $post) {
// 현재 사용자는 게시물을 업데이트할 수 있습니다...
})->can('update', 'post');

모델이 필요 없는 미들웨어 작업

다시 말하지만, create와 같은 일부 정책 메서드는 모델 인스턴스가 필요하지 않습니다. 이러한 경우 미들웨어에 클래스 이름을 전달할 수 있습니다. 클래스 이름은 작업을 승인할 때 사용할 정책을 결정하는 데 사용됩니다.

Route::post('/post', function () {
// 현재 사용자는 게시물을 만들 수 있습니다...
})->middleware('can:create,App\Models\Post');

문자열 미들웨어 정의 내에서 전체 클래스 이름을 지정하는 것은 번거로울 수 있습니다. 따라서 can 메서드를 사용하여 라우트에 can 미들웨어를 연결할 수 있습니다.

use App\Models\Post;
 
Route::post('/post', function () {
// 현재 사용자는 게시물을 만들 수 있습니다...
})->can('create', Post::class);

Blade 템플릿을 통해

Blade 템플릿을 작성할 때 사용자가 주어진 작업을 수행할 권한이 있는 경우에만 페이지의 일부를 표시하고 싶을 수 있습니다. 예를 들어, 사용자가 실제로 게시물을 업데이트할 수 있는 경우에만 블로그 게시물에 대한 업데이트 양식을 표시하고 싶을 수 있습니다. 이 경우 @can@cannot 지시문을 사용할 수 있습니다.

@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 구문은 다음 구문과 동일합니다.

@if (Auth::user()->can('update', $post))
<!-- 현재 사용자는 게시물을 업데이트할 수 있습니다... -->
@endif
 
@unless (Auth::user()->can('update', $post))
<!-- 현재 사용자는 게시물을 업데이트할 수 없습니다... -->
@endunless

사용자가 주어진 작업 배열에서 작업을 수행할 권한이 있는지 확인할 수도 있습니다. 이를 위해 @canany 지시어를 사용하세요.

@canany(['update', 'view', 'delete'], $post)
<!-- 현재 사용자는 게시물을 업데이트, 보기 또는 삭제할 수 있습니다... -->
@elsecanany(['create'], \App\Models\Post::class)
<!-- 현재 사용자는 게시물을 생성할 수 있습니다... -->
@endcanany

모델이 필요하지 않은 액션

다른 대부분의 권한 부여 방법과 마찬가지로, 액션에 모델 인스턴스가 필요하지 않은 경우 @can@cannot 지시어에 클래스 이름을 전달할 수 있습니다.

@can('create', App\Models\Post::class)
<!-- 현재 사용자는 게시물을 생성할 수 있습니다... -->
@endcan
 
@cannot('create', App\Models\Post::class)
<!-- 현재 사용자는 게시물을 생성할 수 없습니다... -->
@endcannot

추가 컨텍스트 제공

정책을 사용하여 작업을 승인할 때 다양한 승인 기능 및 헬퍼에 배열을 두 번째 인수로 전달할 수 있습니다. 배열의 첫 번째 요소는 어떤 정책을 호출해야 하는지 결정하는 데 사용되고, 나머지 배열 요소는 정책 메서드에 매개변수로 전달되어 승인 결정을 내릴 때 추가 컨텍스트로 사용할 수 있습니다. 예를 들어 추가 $category 매개변수를 포함하는 다음 PostPolicy 메서드 정의를 고려해 보십시오.

/**
* 사용자가 주어진 게시물을 업데이트할 수 있는지 확인합니다.
*/
public function update(User $user, Post $post, int $category): bool
{
return $user->id === $post->user_id &&
$user->canUpdateCategory($category);
}

인증된 사용자가 주어진 게시물을 업데이트할 수 있는지 확인하려고 할 때 다음과 같이 이 정책 메서드를 호출할 수 있습니다.

/**
* 주어진 블로그 게시물을 업데이트합니다.
*
* @throws \Illuminate\Auth\Access\AuthorizationException
*/
public function update(Request $request, Post $post): RedirectResponse
{
Gate::authorize('update', [$post, $request->category]);
 
// 현재 사용자는 블로그 게시물을 업데이트할 수 있습니다...
 
return redirect('/posts');
}

승인 및 Inertia

승인은 항상 서버에서 처리해야 하지만, 프런트엔드 애플리케이션의 UI를 올바르게 렌더링하기 위해 승인 데이터를 제공하는 것이 편리할 수 있습니다. Laravel은 Inertia 기반 프런트엔드에 승인 정보를 노출하기 위한 필수 규칙을 정의하지 않습니다.

그러나 Laravel의 Inertia 기반 스타터 키트 중 하나를 사용하는 경우, 애플리케이션에는 이미 HandleInertiaRequests 미들웨어가 포함되어 있습니다. 이 미들웨어의 share 메서드 내에서 애플리케이션의 모든 Inertia 페이지에 제공될 공유 데이터를 반환할 수 있습니다. 이 공유 데이터는 사용자에 대한 승인 정보를 정의하는 데 편리한 위치 역할을 할 수 있습니다.

<?php
 
namespace App\Http\Middleware;
 
use App\Models\Post;
use Illuminate\Http\Request;
use Inertia\Middleware;
 
class HandleInertiaRequests extends Middleware
{
// ...
 
/**
* 기본적으로 공유되는 props를 정의합니다.
*
* @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),
],
],
],
];
}
}