라라벨 정책 설정으로 역할별 접근 제어하기
2025-03-09 | 라라벨 정책 설정으로 역할별 접근 제어하기

라라벨의 policy 정책 설정을 통해서 관리자만 접근할 수 있도록 설정할 수 있다. 예시와 다르게 회원별 등급을 만들어 역할별 제어(RBAC)도 구현할 수 있다.
user 테이블 마이그레이션
먼저 기본적으로 생성된 Users 테이블에는 역할에 대한 필드가 없으므로, migration파일에서 추가해준다.
Schema::create('users', function (Blueprint $table) {
...
$table->enum('role', ['admin', 'user'])->default('user');
...
});
role 필드는 admin과 user 두가지 값을 갖는데, 별도로 설정하지 않는다면 user 값이 기본이 된다. 이제 회원가입을 통해서는 관리자 role을 가질 수 없으므로, seeder를 이용해서 관리자 계정을 생성한다.
php artisan make:seeder UserSeeder
UserSeeder 파일에서 아래와 같이 생성할 사용자 정보를 추가해준다.
// database/seeders/UserSeeder.php
<?php
namespace Database\Seeders;
use App\Models\User;
use Illuminate\Database\Seeder;
class UserSeeder extends Seeder
{
public function run(): void
{
User::factory()->create([
'name' => 'Admin',
'email' => 'admin@example.com',
'password' => bcrypt('password'),
'role' => 'admin',
'email_verified_at' => now(),
]);
User::factory()->create([
'name' => 'User',
'email' => 'user@example.com',
'password' => bcrypt('password'),
'role' => 'user',
'email_verified_at' => now(),
]);
}
}
테스트 용도로 사용할 관리자 계정과 사용자 계정을 각각 하나씩 만들어주었다.
이제 DatabaseSeeder.php 파일에 실행할 Seeder를 연결해준다.
// database/seeders/DatabaseSeeder.php
<?php
namespace Database\Seeders;
use Illuminate\Database\Seeder;
use Illuminate\Support\Facades\DB;
class DatabaseSeeder extends Seeder
{
public function run(): void
{
// 중복될 경우를 대비해 사용자 테이블에 존재하는 정보를 삭제한다.
DB::table('users')->delete();
// 직전에 만든 UserSeeder를 연결해준다.
$this->call([
UserSeeder::class
]);
}
}
이제 아래 명령으로 seed 작업을 실행한다.
php artisan migrate #만약 아직 DB 연결이 안됐다면 실행.
php artisan db:seed #seed 작업 실행
users 테이블을 확인해보면 정상적으로 테스트 계정 두개가 생성된 것을 확인할 수 있다.
Policy 정책 설정
먼저 아래 명령으로 정책을 설정할 PostPolicy.php 파일을 생성해준다. --model=Post 라는 인수를 전달해주면, Post 모델과 연결된 정책을 알아서 설정해 파일을 생성해줘서 간편하다.
php artisan make:policy PostPolicy --model=Post
app/Policies/PostPolicy.php 파일이 생성되었을 것이다. 좀 더 편리하게 작업하기 위해 User 모델에서 사전작업을 해준다.
<?php
namespace App\Models;
// ... 생략 ...class User extends Authenticatable
{
// ... 생략 ...
public function isAdministrator():bool{
return $this->role === 'admin';
}
isAdministrator라는 메소드를 정의해준다. 사용자의 역할이 admin인지 체크하여 true,false를 반환해주는 메소드이다. 이 메소드를 이용해 PostPolicy를 아래와 같이 정의한다.
<?php
namespace App\Policies;
use App\Models\Post;
use App\Models\User;
class PostPolicy
{
public function before(User $user, string $ability): bool|null
{
if ($user->isAdministrator()) {
return true;
}
return null;
}
public function viewAny(User $user): bool
{
return false;
}
public function view(User $user, Post $post): bool
{
return $post->status === true;
}
public function create(User $user): bool
{
return false;
}
public function store(User $user): bool
{
return false;
}
public function update(User $user, Post $post): bool
{
return false;
}
public function delete(User $user, Post $post): bool
{
return false;
}
public function restore(User $user, Post $post): bool
{
return false;
}
public function forceDelete(User $user, Post $post): bool
{
return false;
}
}
--model=Post 인수로 생성된 파일이기 때문에 기본적으로 모델과 연결된 정책들이 설정되어있는데, 모두 return false 즉, 정책상 접근하지 못하도록 설정되어있다. show 메소드는 게시물의 상태가 발행인지 미발행인지만 체크한다. 하지만 before 메소드를 이용해, 모든 메소드 이전에 확인작업을 거친다.
User모델에서 설정해준 isAdministrator() 메소드를 이용해 admin role을 가진 사람은 모두 true로 설정된다. 따라서 관리자만 접근할 수 있고, 나머지는 모두 false로 반환되는 정책이 설정되었다.
AuthServiceProvider 설정
정책을 설정했다고해서 바로 적용되는게 아니다. AuthServiceProvider 파일을 만들어준다.
php artisan make:provider AuthServiceProvider
// app/Providers/AuthServiceProvider.php
<?php
namespace App\Providers;
use App\Models\Post;
use App\Policies\PostPolicy;
use Illuminate\Foundation\Support\Providers\AuthServiceProvider as ServiceProvider;
class AuthServiceProvider extends ServiceProvider
{
protected $policies = [
Post::class => PostPolicy::class //방금 만들어준 정책을 배열에 추가한다.
];
//생략
public function boot(): void
{
$this->registerPolicies(); //추가된 정책을 등록한다.
}
}
정책 적용
정책을 만들고, 등록하였으니 이제 정책을 어디에 사용할 것인지 설정해줄 수 있다.
<?php
namespace App\Http\Controllers;
use App\Models\Post;
use Illuminate\Http\RedirectResponse;
use Illuminate\Http\Request;
use Illuminate\Foundation\Auth\Access\AuthorizesRequests;
use Inertia\Inertia;
use Inertia\Response;
class PostController extends Controller
{
use AuthorizesRequests; // Authorize() 메소드를 사용하기 위해 설정.
public function create(): Response
{
$this->authorize('create', Post::class);
// 생략
}
public function store(Request $request):RedirectResponse
{
$this->authorize('store', Post::class);
// 생략
}
public function edit(Post $post) : Response
{
$this->authorize('update', $post);
// 생략
}
public function update(Request $request, Post $post) : RedirectResponse
{
$this->authorize('update', $post);
// 생략
}
public function destroy(Post $post) {
$this->authorize('delete', $post);
// 생략
}
}
use AuthorizesRequests 를 통해 $this->authorize() 구문을 사용할 수 있다. authorize 메소드를 이용해 아까 만들어준 PostPolicy의 정책 필터를 통과하게 되는데, create, store, update, delete 는 관리자만 통과할 수 있는 정책으로 적용된다.
admin 권한이 없는 사람이 페이지에 접근하거나, 메소드에 접근하게 되면 403 에러를 출력하며 권한이 없음을 알린다.