라라벨 SPATIE 권한 관리, 사용자별 글 작성·수정·삭제 제어 및 관리자 전체 권한 부여

포스트 썸네일 이미지

지금까지 라라벨Laravel 프로젝트에 SPATIE Laravel Permission도 설치하면서, 역할(Role)과 권한(Permission)을 부여하는 기능까지 구현했다.

하지만 중간에 다른 기능들도 함께 구현하느라 정작 역할과 권한을 부여해서 뭐에 써먹는 건지, 실용적인 기능은 아직 만들지 못했다.


오늘의 포스트에서는 드디어 역할과 권한을 이용해서 실용적인 기능을 하나 구현해 보려고 한다.

먼저 글 작성 권한이 없는 회원은 글을 작성하지 못하게 하는 기능을 구현해 보겠다.




이전 포스트




전체적인 코드 수정은 이 깃허브 링크들을 통해서 확인할 수 있다.




권한이 있는 사용자에게만 글쓰기 버튼을 표시하고 글을 작성할 수 있게 하기


한 회원에게 banned 역할을 부여했다

먼저 이 기능을 구현하기 전에 미리 되어있어야 하는 사전 작업이 있다.

한 사용자에게 banned 역할을 부여해놓아야 한다.




2달 전에 작성한 이 포스트에서 'admin', 'moderator', 'user', 'banned' 이렇게 네 가지 역할Role을 만들었었다.





user 역할에는 글의 작성과 수정・삭제의 권한을 줬다

그리고 user라는 역할Role에는 글을 작성하고, 수정하고, 삭제할 수 있는 모든 권한을 주고,




banned 역할에는 아무런 권한도 주지 않았다

banned 역할에는 아무런 권한도 주지 않아야 한다.




app/Policies/ArticlePolicy.php

public function create(User $user): bool
{
    return $user->hasPermissionTo('create article');
}

특정 사용자가 글을 생성할 수 있는 권한이 있는지를 확인하는 create 메서드에 코드를 추가했다.


이 코드는 사용자가 'create article'이라는 특정 권한을 가지고 있는지를 확인한다.

hasPermissionTo() 메서드는 SPATIE Laravel Permission 패키지에서 제공되는 메서드로, 해당 사용자가 특정 권한을 가지고 있는지 확인해서 권한이 있다면 true를 반환하고, 권한이 없다면 false를 반환한다.


즉, 이 create 메서드는 사용자가 'create article'이라는 권한을 가지고 있는 경우에만 글을 생성할 수 있게 한다.





resources/views/pages/articles/⚡new.blade.php

Gate::authorize('create', Article::class);

이 코드를 mount 메서드와 save 메서드에 넣었다.


save 메서드에 이 코드를 넣으면, ArticlePolicy의 create 메서드에 의해 'create article' 권한이 없는 사용자가 글을 작성하고 '저장' 버튼을 클릭하면 403 에러가 뜨면서 글이 저장되지 않는다.

또 mount 메서드에도 넣으면 글쓰기 페이지로 갈 경우에도 403 에러가 떠서 글을 작성하는 페이지로 아예 이동조차 할 수 없다.




이 코드는 다른 사용자의 글을 수정이나 삭제할 수 없도록 했을 때 사용했던 코드와 거의 같다.

그런데 다른 점이 딱 하나 있다.

바로, 글 수정과 삭제 때에는 $this->article이 들어갔던 부분에 Article::class가 들어갔다는 것이다.


새로운 글을 작성할 때는 아직 구체적인 Article 인스턴스가 존재하지 않는다.

그래서 Article::class를 전달하여, Article 모델 자체에 대해 권한이 있는지 확인하는 것이다.


글을 수정하거나 삭제할 때는 이미 데이터베이스에 존재하는 특정 Article 인스턴스가 있어야 한다.

그래서 그 경우에는 수정하려는 특정 Article 객체($this->article)를 전달하여, 그 객체에 대한 권한을 확인한다.


즉, 새로운 Article을 작성할 때는 Article 자체에 대한 권한을 검사하고, 수정할 때는 특정 Article 객체에 대한 권한을 검사하는 차이다.




resources/views/pages/articles/⚡list.blade.php

@can('create article')
<div class="flex items-center justify-end mb-3">
    <x-primary-button :href="route('articles.new')" wire:navigate>
        {{ __('Create') }}
    </x-primary-button>
</div>
@endcan

글쓰기 버튼을 @can 디렉티브로 감쌌다.


@can('create article') 블록은 사용자가 create article 권한을 가지고 있는 경우에만 내부의 내용을 렌더링 한다.

따라서 'create article' 권한이 있는 사용자에게만 글쓰기 버튼이 보이게 된다.





banned 역할의 사용자에게는 글쓰기 버튼이 보이지 않게 되었다

banned 역할의 사용자(차단된 사용자)의 계정으로 로그인했다.

그러면 일반적으로는 글쓰기 버튼이 있던 곳에 이렇게 버튼이 사라지게 된다.




권한이 있는 사용자에게만 글 수정과 삭제 허용하기


app/Policies/ArticlePolicy.php

public function update(User $user, Article $article): bool
{
    return $user->id === $article->user_id && $user->hasPermissionTo('update article');
}

public function delete(User $user, Article $article): bool
{
    return $user->id === $article->user_id && $user->hasPermissionTo('delete article');
}

$user->id === $article->user_id는 이전에 추가했던 코드다.

(위에 링크해 놓은 포스트, '라라벨 Livewire 4, 글 삭제 기능 구현 및 Policy 권한 제어'에서 추가했었다.)


거기에 && 연산자를 추가해서 글을 작성한 본인임과 동시에 'update article'과 'delete article' 권한을 가진 사용자만 글을 수정하고 삭제할 수 있도록 수정했다.




관리자에게는 모든 권한 주기 (feat. before 메서드)


app/Policies/ArticlePolicy.php

public function before(User $user, string $ability): bool|null
{
    if ($user->hasRole('admin')) {
        return true;
    }

    return null;
}

원래는 없었던 before라는 메서드를 만들었다.


Policy 클래스 내부에 before 메서드를 정의해 두면, 다른 어떤 권한 메서드(create, update, delete 등)가 실행되기 직전에 무조건 먼저 검사를 수행한다.

이곳에서 이렇게 admin 역할(관리자)에게 true를 반환하도록 만들면, 아래의 update나 delete 개별 로직을 일일이 수정하지 않아도 관리자는 항상 모든 권한을 통과하게 된다.





before 메서드에 대한 Laravel 공식 웹사이트의 설명은 이 링크를 확인하자.




수정 전의 Article 리소스 테이블

이전의 이전 포스트에서 캡처했던, before 메서드를 만들기 전의 Article 리소스 테이블의 모습이다.

관리자인데도 다른 사용자의 글을 삭제할 수 있는 버튼이 보이지 않는다.


하지만, (클릭)

사실 이 상태에서도 다른 사용자의 글을 삭제할 수는 있다.

삭제할 글의 왼쪽에 있는 체크박스를 체크한 다음에 '선택한 항목 삭제'를 클릭하면 된다.




이제 관리자는 Article 리소스 테이블에서 다른 사용자의 글을 삭제할 수 있다

before 메서드가 생긴 후의 Article 리소스 테이블의 모습이다.

이제는 Article 리소스 테이블에서 모든 글에 '수정'과 '삭제' 등의 버튼이 생겨서 삭제해야 할 글을 빠르게 삭제할 수 있게 됐다.




다음 포스트

이 글이 도움이 됐거나 유익했다면 스크롤을 조금만 더 내려서 댓글을 남겨주세요. (비로그인도 가능합니다!)
응원이나 피드백이 담긴 댓글은 제가 계속 블로그를 해나갈 수 있는 원동력이 됩니다. 😊

지인에게 보여주고 싶은 글이었다면 URL을 복사해서 메신저나 소셜 미디어에 공유해 주세요.
이전 포스트 다음 포스트

댓글 쓰기

0 Comments

문의하기 양식