Spring 7기 프로젝트/뉴스피드 팀 프로젝트

Spring Security에서 @PreAuthorize와 별도 권한 컴포넌트를 활용한 동적 인가 구현법

JuNo_12 2025. 5. 28. 16:19

1. 서론

Spring Security는 인증(Authentication)과 인가(Authorization)를 강력하게 지원하는 프레임워크입니다.
특히, 복잡한 권한 요구사항을 깔끔하고 유지보수하기 쉽게 처리하기 위해 @PreAuthorize 애노테이션과 별도의 권한 검증 컴포넌트를 함께 사용하는 동적 인가 기법이 많이 활용됩니다.


2. 동적 인가란?

  • 동적 인가(Dynamic Authorization) 란, 요청에 포함된 사용자 정보와 요청 파라미터, 리소스 상태 등을 실시간으로 판단해 권한 여부를 결정하는 방식입니다.
  • 예:
    • 댓글 수정 요청 시, 해당 댓글의 작성자인지 확인
    • 특정 문서에 접근 시, 문서 상태 및 사용자 역할에 따라 접근 권한 부여 등

3. @PreAuthorize + 별도 권한 컴포넌트

1. @PreAuthorize 애노테이션

  • 메서드 호출 전, SpEL(Spring Expression Language)로 권한 조건을 평가합니다.
  • 선언적으로 권한 정책을 분리할 수 있습니다.

2. 별도 권한 검증 컴포넌트

  • 권한 검증을 위한 로직을 별도 컴포넌트에 모아 재사용성 및 유지보수를 용이하게 만듭니다.
  • 예시: CommentSecurity 클래스

4. 구현 예시

1. 권한 검증 컴포넌트 (CommentSecurity.java)

@Component
@RequiredArgsConstructor
public class CommentSecurity {

    private final CommentRepository commentRepository;

    public boolean isCommentOwner(Long commentId) {
        Authentication authentication = SecurityContextHolder.getContext().getAuthentication();

        if (authentication == null || !authentication.isAuthenticated()) return false;

        CustomUserDetails userDetails = (CustomUserDetails) authentication.getPrincipal();
        Long userId = userDetails.getUserId();

        return commentRepository.existsByIdAndUserId(commentId, userId);
    }
}

2. 컨트롤러에서 @PreAuthorize 사용

@PreAuthorize("@commentSecurity.isCommentOwner(#commentId)")
@PatchMapping("/comments/{commentId}")
public ResponseEntity<Void> updateComment(
        // 비지니스 로직만 집중
}

 

@Component
public class PostService {
    public boolean isOwner(Long postId, String username) {
        Post post = postRepository.findById(postId)
            .orElseThrow(() -> new PostNotFoundException());
        return post.getAuthor().getUsername().equals(username);
    }
}

5. 장점

  • 선언적 보안: 메서드 레벨에서 보안 규칙을 명시적으로 선언
  • AOP 기반: 비즈니스 로직과 보안 로직의 완전한 분리
  • 코드 가독성: 어떤 권한이 필요한지 메서드 시그니처에서 바로 확인 가능
  • 일관성: 다른 권한 검증과 동일한 패턴 사용
  • 테스트 용이성: 보안 설정을 모킹하여 단위 테스트 가능
  • 확장성: 나중에 관리자 권한, 그룹 권한 등이 추가되어도 표현식만 수정하면 됨\
  • 보안 표준: Spring Security의 표준 접근 방식
  • 재사용성: 다른 엔티티(댓글, 파일 등)에도 동일한 패턴 적용 가능

더보기

선언적(Declarative) 권한 정책이란?

  • 선언적이란, 코드 안에서 무엇을 할 것인지를 명확하게 '선언'하는 방식을 말합니다.
  • 반대로, 명령적(Imperative)은 어떻게 할 것인지 절차를 직접 작성하는 방식입니다.

예를 들어,

  • 명령적 코드:
    서비스 메서드 안에서 직접 if문으로 권한 체크를 하고, 조건문과 예외 처리를 상세히 작성하는 방식
  • 선언적 코드:
    메서드 위에 @PreAuthorize("조건") 애노테이션만 붙여서 "이 메서드는 이런 조건을 만족하는 사용자만 실행 가능하다"라고 선언하는 방식

그래서 "선언적으로 분리"되면?

  • 권한 체크가 서비스 비즈니스 로직과 분리되어
  • 권한 검증 로직을 메서드 선언부(애노테이션 등)에서 한눈에 볼 수 있어서
  • 코드를 읽을 때 "이 메서드는 어떤 권한이 필요한지"를 직관적으로 파악할 수 있다는 뜻입니다.

가독성이 높아진다?

  • 권한 검사 코드가 메서드 내부에 숨어있지 않고,
  • 메서드 선언부 위에 명확하게 적혀 있어서
  • 코드가 더 깔끔하고 이해하기 쉬워진다는 의미입니다.