인증(Authentication) 은 사용자가 “누구인가”를 확인합니다. 로그인 처리가 그 대표입니다.
인가(Authorization) 는 그 사용자가 “무엇을 할 수 있는가”를 판단합니다. 예를 들어 자신의 게시글만 편집할 수 있고, 관리자만 설정을 변경할 수 있는 것과 같은 제어입니다.인증으로 로그인 기능을 구현했다면 다음 단계가 인가입니다.
Laravel의 인가 기능은 게이트(Gates)와 폴리시(Policies)의 두 가지 접근 방식을 제공합니다. 어느 쪽을 사용할지는 유스케이스에 따라 판단합니다.
권한이 없는 경우 자동으로 403 응답을 반환하려면 Gate::authorize()를 사용합니다. abort(403)을 쓰는 수고가 줄어듭니다.
public function update(Request $request, Post $post): RedirectResponse{ Gate::authorize('update-post', $post); // 권한이 있으면 여기에 도달합니다 // 게시글을 업데이트하는 처리... return redirect('/posts');}
Gate::authorize()는 권한이 없는 경우 Illuminate\Auth\Access\AuthorizationException을 던집니다. Laravel은 이를 자동으로 403 HTTP 응답으로 변환합니다.
--model 옵션으로 생성한 폴리시에는 표준적인 CRUD 액션에 대응하는 메서드가 포함되어 있습니다.
<?phpnamespace App\Policies;use App\Models\Post;use App\Models\User;class PostPolicy{ /** * 게시글 목록을 열람할 수 있는가 */ public function viewAny(User $user): bool { return true; // 인증된 사용자는 누구나 열람 가능 } /** * 특정 게시글을 열람할 수 있는가 */ public function view(User $user, Post $post): bool { return true; // 인증된 사용자는 누구나 열람 가능 } /** * 게시글을 작성할 수 있는가 */ public function create(User $user): bool { return true; // 인증된 사용자는 누구나 투고 가능 } /** * 게시글을 업데이트할 수 있는가 */ public function update(User $user, Post $post): bool { return $user->id === $post->user_id; // 자신의 게시글만 편집 가능 } /** * 게시글을 삭제할 수 있는가 */ public function delete(User $user, Post $post): bool { return $user->id === $post->user_id; // 자신의 게시글만 삭제 가능 }}
폴리시에서도 before 메서드를 정의함으로써 관리자에게 모든 권한을 부여할 수 있습니다.
/** * 다른 폴리시 메서드보다 먼저 실행되는 사전 체크 * $ability 에는 폴리시 메서드명('update', 'delete' 등)이 들어감 */public function before(User $user, string $ability): bool|null{ if ($user->isAdministrator()) { return true; // 관리자는 모든 조작을 허가 } return null; // null 을 반환하면 일반 폴리시 메서드로 진행}
before 메서드는 폴리시 클래스에 대응하는 메서드가 존재하는 경우에만 호출됩니다. 예를 들어 update 메서드가 존재하지 않는 경우 before도 호출되지 않습니다.
use App\Models\Post;// 게시글의 업데이트 권한이 있는 사용자만 접근 가능Route::put('/posts/{post}', [PostController::class, 'update']) ->middleware('can:update,post');// 게시글의 작성 권한이 있는 사용자만 접근 가능Route::post('/posts', [PostController::class, 'store']) ->middleware('can:create,App\Models\Post');
블로그 앱에서 “투고자만 자신의 기사를 편집·삭제할 수 있는” 권한을 구현하는 예시입니다.
1
폴리시를 생성
php artisan make:policy PostPolicy --model=Post
2
폴리시 메서드를 구현
<?phpnamespace App\Policies;use App\Models\Post;use App\Models\User;class PostPolicy{ /** * 관리자에게 모든 권한을 부여 */ public function before(User $user, string $ability): bool|null { if ($user->is_admin) { return true; } return null; } public function viewAny(User $user): bool { return true; } public function view(User $user, Post $post): bool { return true; } public function create(User $user): bool { return true; } public function update(User $user, Post $post): bool { return $user->id === $post->user_id; } public function delete(User $user, Post $post): bool { return $user->id === $post->user_id; }}
3
컨트롤러에 authorizeResource() 를 추가
<?phpnamespace App\Http\Controllers;use App\Models\Post;use Illuminate\Http\RedirectResponse;use Illuminate\Http\Request;use Illuminate\View\View;class PostController extends Controller{ public function __construct() { $this->authorizeResource(Post::class, 'post'); } public function index(): View { $posts = Post::latest()->paginate(10); return view('posts.index', compact('posts')); } public function store(Request $request): RedirectResponse { $post = $request->user()->posts()->create( $request->validate([ 'title' => ['required', 'string', 'max:255'], 'body' => ['required', 'string'], ]) ); return redirect()->route('posts.show', $post); } public function update(Request $request, Post $post): RedirectResponse { $post->update( $request->validate([ 'title' => ['required', 'string', 'max:255'], 'body' => ['required', 'string'], ]) ); return redirect()->route('posts.show', $post); } public function destroy(Post $post): RedirectResponse { $post->delete(); return redirect()->route('posts.index'); }}