소스코드에 제공되는 메타데이터이다. 앱이 처리하는 데이터가 아닌 컴파일 과정,실행 과정에서 코드를 어떻게 처리해야 하는지 알려주는 용도로 사용된다.
ex ) @Controller @Service
이러한 어노테이션을 사용자가 직접 정의 하여 구현 할 수도 있다.
특정 권한을 체크하는 커스텀 어노테이션을 만들어 적용해보자.
// 커스텀 어노테이션 선언
@Retention(RetentionPolicy.RUNTIME) // 2
@Target({ElementType.TYPE, ElementType.METHOD}) // 3
public @interface CheckRole { // 1
Authority auth();
}
------------------------------------------------------------
// 권한 클래스
@AllArgsConstructor
@Getter
public enum Authority {
ADMIN("ROLE_ADMIN"),
USER("ROLE_USER");
private String value;
}
@Aspect
@Component
@RequiredArgsConstructor
public class CheckRoleAop {
private final MemberRepository memberRepository;
private final JwtTokenProvider tokenProvider;
// 1
@Pointcut("execution(* com.kjw.shop..*.*(..))")
private void cut(){}
// 2
@Before("cut() && @annotation(auth)")
public void before(JoinPoint joinPoint,CheckRole auth) {
// 3
HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.currentRequestAttributes()).getRequest();
String header = request.getHeader("X-AUTH-TOKEN");
Member member = memberRepository.findById(tokenProvider.getUserPk(header))
.orElseThrow(() -> new IllegalArgumentException("error"));
// 4
String checkRole = auth.auth().getValue();
String memberRole = member.getRoles().get(0).getName().getValue();
System.out.println("checkRole = " + checkRole);
System.out.println("memberRole = " + memberRole);
}
}
방금 만든 어노테이션이 붙은 메서드가 실행되기 전 동작하는 AOP 이다.
1. 어떤 JoinPoint 에서 사용 될 것인지 명시해준다.
2. @Before 은 해당 괄호 조건이 실행되기 전에 작동한다. 여기서는 com.kjw.shop 하위 패키지이면서 @CheckRole 커스텀어노테이션 auth가 있는 곳이다.
3. 헤더에있는 토큰으로 사용자 정보를 불러오는 곳.
4. 실제 커스텀 어노테이션에서 제한한 권한과 사용자의 권한 비교
@RestController
@RequestMapping(value = "/admin")
public class HomeController {
@GetMapping("/test")
@CheckRole(auth = Authority.ADMIN)
public String test() {
return "hello";
}
}
커스텀 어노테이션을 만들어 권한이 다르면 예외를 던지거나 로그를 찍을수도있다.
사용자 입맛에 맞게 구현하면 된다.