본문 바로가기

Spring

Spring AOP를 활용한 권한 체크

Controller 단에서 권한 체크를 한다면 보통 아래와 같이 if문으로 체크를 할 것 입니다.

여러 곳에서 이러한 if문으로 계속 처리하다보면 중복이 많아지는 문제가 발생해 aop를 활용해 어노테이션으로 처리하는 방법을 알아보도록하겠습니다.

@GetMapping
public String example(@AuthenticationPrincipal UserPrincipal account){
	Grade grade = account.getGrade();
	if(grade != Grade.HEAD){
	    throw new UnauthorizedException();
    }
    
    return "";
}

aop dependency는 spring-boot-starter를 dependency햇다면 안에 내장되어 있습니다.

사용할 어노테이션 설정 GradeMapping

  • 어노테이션을 사용하는 방식을 사용하도록 하겠습니다.
  • 사용할 어노테션과 내부 메서드를 선언합니다.
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface GradeMapping {

    Grade[] include() default {};

    boolean isAdmins() default false;

}

@Aspect 클래스 

  • @Aspect 클래스를 빈으로 등록하고 메소드 실행 전 권한을 체크해야하기 때문에 @Before을 사용합니다.
  • 사용할 어노테이션을 매핑해줍니다.
  • JoinPoint를 사용하여 타겟메소드를 리플렉션하여 어노테이션에 설정된 값을 가져옵니다.
  • 그 값을 활용하여 권한 체크를 해주고, 권한이 틀리면 익셉션을 날려줍니다 .
@Aspect
@Component
@RequiredArgsConstructor
@Slf4j
public class PermissionCheckAop {

    private final UserPrincipal info;

    @Before(value = "@annotation(com.annotation.GradeMapping)")
    public void permissionCheck(JoinPoint joinPoint) {
        final MethodSignature signature = (MethodSignature)joinPoint.getSignature();
        final Method method = signature.getMethod();

        final GradeMapping annotation = method.getAnnotation(GradeMapping.class);
        final Grade[] methodGradeArr = annotation.include();

        final Grade grade = info.getGrade();

        if (annotation.isAdmins()) {
            if (grade.ADMIN || grade.관리자 권한들) {
                return;
            }
        }
        if (Arrays.stream(methodGradeArr).anyMatch(e -> e == grade)) {
            return;
        }

        log.error(">>>>>PERMISSION CHECK ERROR : Account Grade = {} , Target Method Name = {}",
						 grade, method.getName());
        throw new UnauthorizedException("해당 접근에 대한 권한이 없습니다.");
    }
}

사용할 메소드에 작성한 어노테이션 설정

  • 이와같이 간편하게 권한 체크를 할 수 있게 됩니다.
@GetMapping
@GradeMapping(inclue = {Grade.ADMIN,Grade.AGENT,GRADE.USER})
public String example() {
    return "";
}


@GetMapping
@GradeMapping(isAdmins = true)
public String example() {
    return "";
}

 

 

추가 사항 - 메서드를 포함한 클래스 위에 어노테이션을 달아서 권한을 검증하고싶을 때

  • @Target에 ElementType.Type을 추가하면 클래스 딴에 어노테이션을 추가할 수 있다. 

  • 클레스 단에 붙은 Annotation의 value()를 알고싶으면  joinPoint.getTarget().getClass().getAnnotation(어노테이션 클래스 명)으로 Annotation 정보를 가져올 수 있다. 
  • @within : 클래스 혹은 인터페이스 단위까지만 범위 지정이 가능하다.
@Before(value = "@within(com.udp.estate.aop.annotation.GradeMapping) ||@annotation(com.udp.estate.aop.annotation.GradeMapping)")
public void permissionCheckByExecution(JoinPoint joinPoint) {
    final MethodSignature signature = (MethodSignature)joinPoint.getSignature();
    final Method method = signature.getMethod();

    final GradeMapping annotation = method.getAnnotation(GradeMapping.class)!= null ? method.getAnnotation(GradeMapping.class) : joinPoint.getTarget().getClass().getAnnotation(GradeMapping.class);

 

 

aop에 관해 참고할만한 블로그

https://jojoldu.tistory.com/71