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에 관해 참고할만한 블로그