자바 뚝딱거리기

[JAVA] 커스텀 어노테이션 만들기.. 그런데 Path Variable를 곁들인

bimppap 2022. 11. 22. 15:44

 

 

 

위와 같이 컨트롤러 단에서 API를 통해 들어오는 jwt 토큰과 Path Variable로 들어오는 유저 아이디가 같은지 확인하고 아니라면 Unauthorized 에러를 보내는 어노테이션을 만드려고 했다. 이전에 Argument Resolver를 통하여 파라미터에 어노테이션을 붙여 사용해 본 적은 있으나, 메소드에 어노테이션은 붙여본 적이 없어 이참에 한번 도전해보기로 했다.

 

서비스단에서 검사하는 대신 커스텀 어노테이션을 쓰는 이유를 간략하게 설명하자면, AOP를 통해 반복되는 코드를 줄이고 재활용성을 높이기 위해서이다. AOP는 Aspect Oriented Programming의 줄임말로 비즈니스 로직에서 핵심 로직이 아닌 공통적인 부분(로깅, 보안 등)을 분리시켜 비즈니스 로직에선 핵심 로직만 집중할 수 있도록 만드는 기술이다. 여기선 보안 때문에 적용하는 중이라 볼 수 있다.

 

그럼 본격적으로 커스텀 어노테이션을 만들어 보자. 먼저 어노테이션을 만든다.

@Target

어노테이션이 적용될 위치를 정하는 메타 어노테이션(어노테이션에 붙이는 어노테이션).

파라미터, 메소드, 생성자 등 다양한 곳에 적용할 수 있다.

@Retention

어노테이션이 어느 시점까지 영향을 미치는지 정하는 메타 어노테이션.

컴파일 전, 클래스 참조, 런타임까지 적용할 수 있다.

@interface

어노테이션 선언. 

 

 

어노테이션이 활용되는 방향과 방식을 정해주는 어드바이스를 정의한다.

@Aspect

스프링 어플리케이션 실행 시점에서 자동 프록시 생성기가 호출되면 이 어노테이션이 붙은 빈을 조회해 어드바이저를 생성한다.

따라서 빈 등록을 위해 @Component가 선행된다.

@Pointcut

어드바이스를 주입시킬 범위를 선정한다.

@Before

어드바이스가 주입될 타겟이 호출되기 전 어드바이스 내용이 주입, 수행된다.

언제 주입 및 수행될지 조건을 정할 수 있다. 위 코드에선 @Pointcut에서 지정한 범위 내에서 LoginMember라는 어노테이션을 달고 있을 경우 실행된다.

JoinPoint

어드바이스가 적용될 수 있는 메서드 또는 그 위치를 담고 있다.

 

before 메소드의 흐름은 다음과 같다.

1. request에서 Authorization 헤더에 담긴 값(=jwt토큰)을 가져온다.
2. jwt토큰에서 userId를 추출한다.
3. request에서 api path에 담긴 variable(s)를 map 형식으로 가져온다.
4. map에서 userId라는 변수를 키로 가진 값을 가져온다.
5. 1~2에서 jwt에서 구한 userId와 3~4에서 path에서 구한 userId를 비교한다.
6. 두 값이 같지 않으면 접근 권한이 없다는 에러를 던진다. 같을 경우 메소드가 종료된다.

 

개인적으로 3번처럼 Object을 Map으로 캐스팅하여 가져오는 건 무식한 방법이라고 보지만 다른 방법은 찾아도 나오지 않거나 제대로 작동하지 않았다. request getAttribute 대신 JoinPoint.getArgs()를 통해 가져올 수도 있지만 메소드 파라미터를 순서대로 가져오기 때문에 위 캡쳐대로 이름을 가져오면 순서가 꼬일 우려가 있어 추천하진 않는다.

 

 

jwtTokenProvider에서 extractId는 아래와 같이 작동한다.

token이 유효한지 검사한 이후(아닐 경우 에러), 'Bearer abc.def.ghi'식으로 되어있을 token에서 abc.def.ghi 부분을 분리해내 시크릿키로 decrypt하면 body에 담겨있던 userId를 받아올 수 있다.

 

끝났다. 이런 식으로 메소드에 어노테이션을 붙여 사용할 수 있다.

@LoginMember를 통과하지 못하면 이렇게 뜬다.

통과할 경우 이렇게 뜬다.

 

참고자료

https://jaimemin.tistory.com/2031

https://stackoverflow.com/questions/43183537/spring-aop-how-to-read-path-variable-value-from-uri-template-in-aspect

https://advenoh.tistory.com/21