Closed seokjin8678 closed 4 months ago
244 files 244 suites 29s :stopwatch: 797 tests 797 :white_check_mark: 0 :zzz: 0 :x: 815 runs 815 :white_check_mark: 0 :zzz: 0 :x:
Results for commit ebd3ce7b.
:recycle: This comment has been updated with latest results.
📌 관련 이슈
✨ PR 세부 내용
이슈에서 적은 내용대로 JWT의 페이로드의 권한에 따른 클레임을 담기 위해 기존 인증 기능을 새롭게 리팩터링 했습니다.
우선 기존 인증 기능의 프로세스를 정리하자면 다음과 같습니다.
AuthInterceptor
접근AuthInterceptor
에 따라 정의된HttpRequestTokenExtractor
에 따라 토큰 추출 2.1 Request Header, Cookie에서 토큰을 추출하는데, 둘 중 하나만 허용, 만약 변경하려면 코드의 수정 필요 -> 문제1AuthPayload
객체 반환 -> 인증AuthPayload
의 Role이 해당 인터셉터가 허용하는 권한과 맞는지 검증 -> 인가AuthenticateContext
에 인가된 사용자의 식별자와 권한 부여@Member
와 같은 에너테이션이 붙어있는Long
타입의 파리미터에RoleArgumentResolver
를 통해AuthenticateContext
에서 부여된 식별자를 할당 6.1 이슈에서 지적한 내용이지만, 식별자로만 받기 때문에 해당 권한의 사용자가 어떠한 권한을 가졌는지 확인하려면 DB를 거쳐야 함 -> 문제2문제1은 간단히
CompositeHttpRequestTokenExtractor
를 사용하는 것으로 해결되었습니다만, 문제2가 이 PR의 핵심입니다.문제2를 해결하려면
AuthenticateContext
에 식별자와 Role 필드뿐 아닌, 해당 권한을 가진 사용자가 가진 값들이 필요합니다.하지만 해당 값들은 권한마다 다를 수 있기에 모든 값을
AuthenticateContext
에 필드로 둘 수 없습니다.따라서
Authentication
이라는 인터페이스를 정의하여 해당 인터페이스를 의존하게 한 뒤,Authentication
를 받는ArgumentResolver
를 정의하여 사용해야 합니다.그러면 권한마다 별도의
Authentication
구현체가 생기는데, 토큰의 권한을 구분하여Authentication
구현체를 생성하는 기능이 필요합니다.따라서 해당 권한을 구현하기 위해
AuthenticationTokenExtractor
을 설계하였고,AuthenticationTokenExtractor
에서 토큰의 권한을 구분하여Authentication
구현체를 반환합니다.이때
AuthenticationTokenExtractor
구현체를 보면 토큰을 파싱하여 바로Authentication
객체를 반환하지 않고, 그저AuthenticationTokenExtractor
구현체를 의존한 뒤, 이를 위임하여 사용하는 것을 볼 수 있습니다.이렇게 구현한 이유는 문제1을 해결한 것과 비슷한데, 토큰을 추출하는 과정에서 권한에 따라 다시 여러 번 토큰을 파싱하지 않게 하기 위함입니다.
구현한 인증 기능을 사용하려면 컨트롤러의 핸들러 메서드에
@Authorization
어노테이션을 사용한 뒤, 해당 어노테이션의 필드에 Role을 추가하면 되는데, 이게 상당히 번잡합니다. 😂 (기존@MemberAuth
어노테이션이 붙은 컨트롤러도 모두 수정해야 하는 문제점도 발생)하지만
HandlerMethod.getMethodAnnotation()
메서드에는 숨겨진 하나의 비밀이 있는데, 해당 메서드에 다음과 같은 주석이 적혀있습니다.이는 타겟으로 한 어노테이션이 아니더라도, 해당 핸들러 메서드의 어노테이션에 타겟으로 하는 어노테이션이 있으면, 해당 어노테이션을 사용할 수 있습니다.
그래서
MemberAuthV1Controller
의 핸들러 메서드에는Authorization
어노테이션이 붙어있지 않은데, 인증과 관련된 테스트가 모두 통과하는 것을 볼 수 있습니다.여러 디자인 패턴이 사용되었고, 인터페이스와 구현체가 많기에 자세한 설명은 코드에 리뷰로 남기겠습니다!