실전 프로젝트 2 - Forum

34강 - 사용자 역할

역할 기능을 이용해 RBAC (== Role Based Access Control)을 구현할 것이다. 앞에서 배운 'auth' 미들웨어가 할 수 있는 것은 특정 엔드포인트에 들어가려면 로그인(== Authentication, 인증)이 필요하다 정도라면, 접근 제어 또는 권한 부여(== Authorization 또는 Access Control)는 특정 엔드포인트는 admin 역할을 가진 사용자만 들어갈 수 있도록 할 수 있다. 우리 프로젝트를 예로 들면, 포럼 글은 로그인한 사용자 누구나 볼 수 있지만, 수정이나 삭제는 admin 또는 소유자만 할 수 있도록 하는 것이 권한 부여의 기능이다.

패키지 설치

사용자에게 역할을 부여하기 위해 bican/roles 패키지를 이용할 것이다. 라라벨에도 Authorization 기능을 지원하고 있으나, 필자에겐 사용법이 너무 어려웠다.

$ composer require "bican/roles: 2.1.*"
// config/app.php

return [
    'providers' => [
        // Other service providers ...
        Bican\Roles\RolesServiceProvider::class,
    ],
];
$ php artisan vendor:publish --provider="Bican\Roles\RolesServiceProvider" --tag=config
$ php artisan vendor:publish --provider="Bican\Roles\RolesServiceProvider" --tag=migrations
$ php artisan migrate

User 모델을 수정하자. 라라벨 네이티브 Authorization을 사용하지 않을 것이므로 AuthorizableContractAuthorizable trait를 삭제하자.

use ...
use Bican\Roles\Traits\HasRoleAndPermission;
use Bican\Roles\Contracts\HasRoleAndPermission as HasRoleAndPermissionContract;

class User extends Model implements AuthenticatableContract,
                                    /*AuthorizableContract,*/
                                    CanResetPasswordContract,
                                    HasRoleAndPermissionContract
{
    use Authenticatable;
    use CanResetPassword;
    use HasRoleAndPermission;
    ...
}

Route 별로 역할에 따른 권한부여를 쉽게 하기 위해서, bican/roles 패키지에 내장되어 배포되는 미들웨어를 app/Http/Kerlen.php 에 등록해 놓자. 나중에 Route에서 'middleware' => 'role:admin' 또는 컨트롤러에서 $this->middleware('role:admin') 처럼 사용할 수 있다.

class Kernel extends HttpKernel
{
    ...
    protected $routeMiddleware = [
        // Other middleware registrations ...
        'role' => \Bican\Roles\Middleware\VerifyRole::class,
    ];
}

역할을 만들자.

관리자모드에서 사용자와 역할을 관리하는 UI를 만드는 것이 좋다. 여기서는 artisan CLI의 tinker 코맨드를 이용한다.

$ php artisan tinker
>>> $admin = Bican\Roles\Models\Role::create([
... 'name' => 'Admin',
... 'slug' => 'admin'
... ]);
=> Bican\Roles\Models\Role {#718
     name: "Admin",
     slug: "admin",
     updated_at: "2015-11-18 06:10:59",
     created_at: "2015-11-18 06:10:59",
     id: 1,
   }
>>> $member = Bican\Roles\Models\Role::create([
... 'name' => 'Member',
... 'slug' => 'member'
... ]);
=> Bican\Roles\Models\Role {#730
     name: "Member",
     slug: "member",
     updated_at: "2015-11-18 06:11:57",
     created_at: "2015-11-18 06:11:57",
     id: 2,
   }

User 13번에 admin 역할을 부여하고, 나머지는 member 역할을 부여할 것이다.

$ php artisan tinker
>>> $users = App\User::where('id', '!=', 13)->get();
>>> $users->map(function($user) {
... $user->roles()->sync([2]);
... });
>>> $user = App\User::find(13);
>>> $user->roles()->sync([1]);

User 와 Role 은 Many to Many 관계를 갖는다. 1명의 User가 여러 개의 Role을 가질 수 있고, admin Role을 가진 User는 여러 명이 있을 수 있다. Forum, Tag, Comment 모델을 다룰 때 뒤에서 다시 살펴볼 것이다.

참고 Bican\Roles\Models\Role 모델과 App\User 모델에 추가한 trait를 잘 따라가보면, 우리가 흔히 보았던 return $this->belongsToMany('App\User') 와 같은 관계 설정을 확인할 수 있다.

map() 메소드는 array_map()과 같은 동작을 한다. Many To Many 관계는 role_user 란 피봇테이블을 이용하여 관계를 기록하는데, $user->roles()->attach(int|array $id) 로 관계를 기록하고, $user->roles()->detach(int|array $id)로 관계를 끊을 수 있다. $user->roles()->sync(array $ids) 메소드는 인자로 넘겨 받은 $ids 목록과 피봇테이블에 써진 관계를 비교해, 인자에 없고 테이블에 있으면 테이블에서 해당 $id들 삭제하고, 인자에 있고 테이블에 없으면 테이블에 해당 $id들을 추가해 준다.

테스트

지금 당장 쓰지 않으니, 모든 기능이 동작하는 지 확인만하고 넘어가도록 하자.

$ php artisan tinker
>>> App\User::find(12)->is('admin'); 
=> false
>>> App\User::find(13)->is('admin');
=> true
>>> App\User::find(13)->is('admin|member', true); 
# == is(['admin', 'member'], true); or isAll(['admin', 'member']);
=> false



comments powered by Disqus
목록 토글
keyboard_arrow_up