클래스에 @CheckRole을 한 경우 특정 메서드는 예외로 할 수 있게 무시 어노테이션을 따로 만들어줌
@Target({ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface IgnoreRoleCheck {}
Aspect 만들기
@Aspect
@Component
public class RoleCheckAspect {
// 클래스와 메서드 단위로 @CheckRole이 붙어있으면 적용
@Around("@within(exe.annotation.CheckRole) || @annotation(exe.annotation.CheckRole)")
public Object checkRole(ProceedingJoinPoint joinPoint) throws Throwable {
Method method = ((MethodSignature) joinPoint.getSignature()).getMethod();
// 무시 어노테이션이 있으면 넘어감
if (method.isAnnotationPresent(IgnoreRoleCheck.class)) {
return joinPoint.proceed();
}
// 해당 메서드 또는 클래스에 @CheckRole이 없다면, 권한 체크를 하지 않고 바로 메서드를 실행
CheckRole checkRole = findCheckRoleAnnotation(method, joinPoint.getTarget().getClass());
if (checkRole == null) {
return joinPoint.proceed();
}
return validateRoleAndProceed(joinPoint, checkRole.value());
}
// 메서드 → 클래스 순서로 @CheckRole 어노테이션을 찾음
private CheckRole findCheckRoleAnnotation(Method method, Class<?> targetClass) {
CheckRole methodAnnotation = method.getAnnotation(CheckRole.class);
if (methodAnnotation != null) {
return methodAnnotation;
}
return targetClass.getAnnotation(CheckRole.class);
}
// 여기서 실제 사용자 인증 정보에서 권한이 있는지 검사
private Object validateRoleAndProceed(ProceedingJoinPoint joinPoint, String requiredRole) throws Throwable {
// 1) 인증 객체 가져오기 => 필터 과정에서 넣어준 권한을 가져옴
Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
if (authentication == null || !authentication.isAuthenticated()) {
throw new Exception("오류");
}
Collection<? extends GrantedAuthority> authorities = authentication.getAuthorities();
String normalizedRequiredRole = requiredRole.startsWith("ROLE_") ? requiredRole : "ROLE_" + requiredRole;
boolean hasRole = authorities.stream()
.anyMatch(auth -> auth.getAuthority().equals(normalizedRequiredRole));
if (!hasRole) {
throw new Exception("오류");
}
return joinPoint.proceed();
}
}