Custom Auth 어노테이션 만들기
목차
1. 개요
Spring Security의 @AuthenticationPrincipal을 대체하는 커스텀 인증 어노테이션을 구현하여, 인증된 사용자 정보를 컨트롤러 메서드에서 간편하게 주입받을 수 있도록 구성했습니다.
2. 구성 요소
1. @UserInfo 어노테이션(Custom Auth 어노테이션 제작)
1
2
3
4
5
6
@Target(ElementType.PARAMETER)
@Retention(RetentionPolicy.RUNTIME)
@Hidden
@Documented
public @interface UserInfo {
}
주요 특징:
@Target(ElementType.PARAMETER): 메서드 파라미터에만 적용 가능@Retention(RetentionPolicy.RUNTIME): 런타임에 리플렉션으로 접근 가능@Hidden: Swagger 문서에서 숨김 처리@Documented: JavaDoc에 포함
2. UserInfoArgumentResolver
Spring MVC의 HandlerMethodArgumentResolver 인터페이스를 구현하여 커스텀 어노테이션이 적용된 파라미터를 자동으로 해석하고 주입
주요 메서드
supportsParameter()
1
2
3
public boolean supportsParameter(MethodParameter parameter) {
return checkParameter(parameter);
}
@UserInfo어노테이션이 적용된 파라미터를 지원할지 판단
resolveArgument()
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
public Object resolveArgument(MethodParameter parameter, ...) {
// 1. @UserInfo 어노테이션 존재 여부 체크
if(!checkParameter(parameter)){
throw new CustomException(ErrorCode.METHOD_NOT_ALLOWED, "Invalid Annotation...");
}
// 2. SecurityContext에서 인증 정보 추출
Authentication auth = SecurityContextHolder.getContext().getAuthentication();
if(auth == null || !auth.isAuthenticated()) {
throw new CustomException(ErrorCode.UNAUTHORIZED);
}
// 3. Principal에서 사용자 정보 추출 (JWT의 경우)
Object principal = auth.getPrincipal();
if(principal instanceof User.MemberVM memberVm) {
return memberVm;
}
throw new CustomException(ErrorCode.UNAUTHORIZED);
}
처리 흐름:
- 어노테이션 유효성 검증
- SecurityContextHolder에서 Authentication 객체 확인
- 인증 여부 검증
- Principal에서
User.MemberVM타입 추출 - 실패 시
CustomException(UNAUTHORIZED)발생
checkParameter()
1
2
3
4
5
6
7
8
private boolean checkParameter(MethodParameter parameter) {
UserInfo userInfoAnnotation = parameter.getParameterAnnotation(UserInfo.class);
boolean hasAnnotation = userInfoAnnotation != null;
boolean isCorrectType = User.MemberVM.class.isAssignableFrom(parameter.getParameterType());
return hasAnnotation && isCorrectType;
}
검증 조건:
@UserInfo어노테이션이 존재하는지 확인- 파라미터 타입이
User.MemberVM타입인지 확인 - 두 조건을 모두 만족해야 true 반환
3. WebMvcConfig 설정
1
2
3
4
5
6
7
8
9
10
@Configuration
@RequiredArgsConstructor
public class WebMvcConfig implements WebMvcConfigurer {
private final UserInfoArgumentResolver userInfoArgumentResolver;
@Override
public void addArgumentResolvers(List<HandlerMethodArgumentResolver> resolvers) {
resolvers.add(userInfoArgumentResolver);
}
}
역할:
UserInfoArgumentResolver를 Spring MVC의 ArgumentResolver 목록에 등록- 의존성 주입을 통해
@Component로 등록된 resolver 사용
4. Controller에서 사용 예시
@AuthenticationPrincipal 사용 (기존 방식)
1
2
3
4
5
6
7
@GetMapping("/member")
public ResponseEntity<CustomResponse<String>> getMethodName(
@AuthenticationPrincipal User.MemberVM memberInfo) {
String name = memberInfo.getName();
CustomResponse<String> res = new CustomResponse<String>(true, null, name);
return ResponseEntity.ok(res);
}
@UserInfo 사용 (커스텀 방식)
1
2
3
4
5
6
7
8
9
@GetMapping("/memberInfo")
public ResponseEntity<CustomResponse<String>> getUserInfo(
HttpServletRequest request,
HttpServletResponse response,
@UserInfo User.MemberVM memberInfo) {
String name = memberInfo.getName();
CustomResponse<String> res = new CustomResponse<String>(true, null, name);
return ResponseEntity.ok(res);
}
3. 동작 원리
sequenceDiagram
participant Client as 클라이언트
participant Filter as Spring Security<br/>Filter Chain
participant Context as SecurityContext
participant Dispatcher as DispatcherServlet
participant Resolver as UserInfoArgument<br/>Resolver
participant Controller as Controller
Client->>Filter: 1. HTTP 요청<br/>(JWT 토큰 포함)
Filter->>Filter: 2. JWT 검증
Filter->>Context: 3. Authentication 저장
Filter->>Dispatcher: 4. 요청 전달
Dispatcher->>Resolver: 5. supportsParameter() 호출
Note over Resolver: @UserInfo 확인<br/>파라미터 타입 검증
Resolver-->>Dispatcher: true 반환
Dispatcher->>Resolver: 6. resolveArgument() 호출
Resolver->>Context: 7. Authentication 조회
Context-->>Resolver: Authentication 반환
Note over Resolver: Principal에서<br/>User.MemberVM 추출
Resolver-->>Dispatcher: User.MemberVM 반환
Dispatcher->>Controller: 8. 메서드 실행<br/>(memberInfo 주입)
Controller-->>Client: 9. 응답 반환
4. 참고 자료
이 기사는 저작권자의 CC BY 4.0 라이센스를 따릅니다.