스프링/security

메서드 권한 설정

진믈리 2024. 8. 9. 04:17

예전에 user들의 권한에 대한 계층구조에 대해 설명한 적이 있다. 계층구조에 대해 설명할때에는 필터를 활용하여 권한을 설정하였지만 시큐리티에서는 메서드를 통한 권한 방법도 제공한다. 그리고 이번에는 소속의 권한까지 추가하여 설명해 보겠다. 예를들어 프론트엔드 팀의 매니저가 백엔드 팀의 유저를 관리하면 조금 이상하지 않을까? 백엔드 팀의 유저는 백엔드 팀의 매니저만 관리 할 수 있게 한번 공부해 보자

 

1. @PreAuthorize

앞서 시큐리티에서는 메서드에서의 권한 설정을 할 수 있다고 했다. 그 방법중에 하나가 바로 @PreAuthorize 어노테이션을 사용하는것이다. 메서드에 @PreAuthorize 와 합께 표현식을 작성한다면 사용자가 메서드에 접근하기 전에 시큐리티가 표현식을 점검하는 것이다.

 

@PreAuthorize("#customUserDetails.getMember().department == T(com.jinmlee.articleProject.enums.Department).FRONTEND")
@GetMapping("/front-only")
public ResponseEntity<String> frontPage(@AuthenticationPrincipal CustomUserDetails customUserDetails){

    return ResponseEntity.ok().body("여기는 프론트 페이지 입니다.");
}

코드를 보면 @PreAuthorize 어노테이션과 식이 적혀있다. 사용자가 /api/front-only 에 접근하면 스프링 시큐리티가 현재 작성된 표현식인 customUserDetails의 소속이 FRONTEND 인지 확인 하는 것이다. 만약 소속이 FRONTEND가 아니라면 403에러가 발생한다.

# 이 붙은 것은 메서드의 매개변수의 값을 가르킨다.

 

1-1. @EnableMethodSecurity

참고로 @PreAuthorize 를 사용하기 위해서는 시큐리티의 보안 메서드 설정을 활성화 하기 위해 SecurityConfig에 @EnableMethodSecurity 을 작성해 줘야 한다.

 

1-2. 권한 여러개 설정하기

@PreAuthorize 는 소속 권한 뿐만 아니라 다양한 권한을 추가할 수 있다. 

@PreAuthorize("hasRole('ADMIN') and #customUserDetails.getMember().department == T(com.jinmlee.articleProject.enums.Department).FRONTEND")

Role 을 검증 하는 로직과 Department 를 검증하는 식이 and를 사이로 두고 작성되어 있다. and는 연결되는 식이 모두 일치해야 시큐리티 검증을 통과 할 수 있다. 이 식은 Role이 ADMIN 이면서 소속이 프론트엔드 야만 검증을 통과할 수 있는 것이다. 만약 사용자의 Role이 여러개라면 hasAnyRole()을 사용하자 일치하는게 하나만 있어도 통과가 된다.
and 뿐만 아니라 or도 작성할 수 있다. or 은 또는 이라는 뜻이다. 만약 and 가 아닌 or 로 작성되어 있다면 Role 이 ADMIN이거나 소속이 프론트엔드이거나 둘중에 하나만 통과해도 식을 통과 할 수 있게 된다.

 

2. 커스텀

커스텀도 가능하다 위에서의 소속 검증 로직을 한번 커스텀해서 만들어보자

@Component
public class Checker {
    
    public boolean validDepartmentFrontend(CustomUserDetails customUserDetails){
        return customUserDetails.getMember().getDepartment() == Department.FRONTEND; 
    }
}

Checker 라는 클래스를 만들었다. @Component 를 통해 클래스를 빈으로 등록시키면 SpEL 상에서 @클래스.메서드() 형태로 사용할 수 있게 된다.

 

@PreAuthorize("hasRole('ADMIN') and @checker.validDepartmentFrontend(#customUserDetails)")
@GetMapping("/front-only")
public ResponseEntity<String> frontPage(@AuthenticationPrincipal CustomUserDetails customUserDetails){

    return ResponseEntity.ok().body("여기는 프론트 페이지 입니다.");
}

@checker.validDepartmentFrontend(#customUserDerails) 이처럼 표현식 내에서 메서드를 사용할 수 있게 된다.

뿐만아니라 다양한 것들도 커스텀 할 수 있다. 예를 들어 내가 작성한 글은 나만 수정가능한데 글과 작성자가 일치하는지 검증하는 로직 등 말이다. 인증 인가 및 공통로직 등을 잘 생각해보고 커스텀도 잘 활용하면 좋을것 같다