이번에 동아리에서 진행하는 프로젝트인 코인
을 Spring 3
에서 Springboot
로 마이그레이션하기로 했다.
레귤러로 전환이 된지 얼마 되지 않은 나는 마이그레이션을 진행하기에 앞서 어떤방식으로 동작하는지에 대해 기존 레귤러들에게 온보딩을 받게 되었다.
온보딩을 진행하면서 몇가지 조사 과제를 받아서 어떤 방식으로 동작하는지 한 번 알아봐야겠다.
코인에서는 @Auth
를 통해 인증?절차를 거친다
자바에서 어노테이션
은 주석이다
실제로 뜻이 주석이다
그런데 어떻게 인증을 하는걸까?
@Override
public ResponseEntity<OwnerResponse> getOwner(
@Auth(permit = {OWNER}) Long ownerId
) {
OwnerResponse ownerInfo = ownerService.getOwner(ownerId);
return ResponseEntity.ok().body(ownerInfo);
}
이렇게 @Auth
를 달아놓으면 정해놓은 로직을 거치게 된다.
@Auth
인터페이스는 별 내용이 없다
@Target(PARAMETER)
@Retention(RUNTIME)
public @interface Auth {
UserType[] permit() default {};
boolean anonymous() default false;
}
이렇게 되어 있는데 @Target
은 어노테이션이 어느곳에 선언될지 지정해주는 것인데 여기서에서는 PARAMETER
에만 적용되도록 설정해주었다.
그리고 @Retention
은 어노테이션의 지속 시간을 정해주는데 여기서는 RUNTIME
까지만 지속되도록 설정한 것을 알 수있다.
이제 그 과정을 한 번 알아보자
그 과정에서는 Argument Resolver
가 사용된다.
HandlerMethodArgumentResolver
라는 인터페이스가 있는데 이를 상속받아 구현해준다
코인에 있는 AuthArgumentResolver
클래스를 가져와서 보자
public class AuthArgumentResolver implements HandlerMethodArgumentResolver {
private final UserRepository userRepository;
private final AuthContext authContext;
@Override
public boolean supportsParameter(MethodParameter parameter) {
return parameter.hasParameterAnnotation(Auth.class);
}
@Override
public Object resolveArgument(MethodParameter parameter, ModelAndViewContainer mavContainer,
NativeWebRequest webRequest, WebDataBinderFactory binderFactory) {
Auth authAt = parameter.getParameterAnnotation(Auth.class);
requireNonNull(authAt);
List<UserType> permitStatus = Arrays.asList(authAt.permit());
Long userId = authContext.getUserId();
if (isAnonymous(userId, authAt)) {
return null;
}
User user = userRepository.getById(userId);
if (permitStatus.contains(user.getUserType())) {
return user.getId();
}
HttpServletRequest request = (HttpServletRequest) webRequest.getNativeRequest();
throw AuthException.withDetail("header: " + request);
}
private static boolean isAnonymous(Long userId, Auth authAt) {
if (userId == null) {
if (authAt.anonymous()) {
return true;
}
throw AuthException.withDetail("userId is null");
}
return false;
}
}
이렇게 되어 있는데 하나씩 뜯어가며 살펴보자
@Override
public boolean supportsParameter(MethodParameter parameter) {
return parameter.hasParameterAnnotation(Auth.class);
}
우선 supportsParameter
라는 메서드를 통해서 파라미터가 @Auth
라는 어노테이션을 가지고 있는지 확인해준다.
@Auth
어노테이션을 가지고 있는게 확인이 된다면 resolveArgument
라는 메서드를 실행시키게 된다.
@Override
public Object resolveArgument(MethodParameter parameter, ModelAndViewContainer mavContainer,
NativeWebRequest webRequest, WebDataBinderFactory binderFactory) {
Auth authAt = parameter.getParameterAnnotation(Auth.class);
requireNonNull(authAt);
List<UserType> permitStatus = Arrays.asList(authAt.permit());
Long userId = authContext.getUserId();
if (isAnonymous(userId, authAt)) {
return null;
}
User user = userRepository.getById(userId);
if (permitStatus.contains(user.getUserType())) {
return user.getId();
}
HttpServletRequest request = (HttpServletRequest) webRequest.getNativeRequest();
throw AuthException.withDetail("header: " + request);
}
@Auth
어노테이션에서는 보통 사용자의 id
가 대상인데 순서대로 살펴보자
Auth authAt = parameter.getParameterAnnotation(Auth.class);
requireNonNull(authAt);
이 부분에서는 어노테이션에 달려있는 파라미터가 null
인지 확인하고 해당 id
를 가져와서 익명
인지 확인한다.
그 후에는 해당하는 id
를 User
객체로 만들어서 해당 아이디를 반환해주는 것을 볼 수 있다.
@IpAddress
로 ip
를 가져오는 어노테이션도 존재한다.
@Hidden // Swagger 문서에 표시하지 않음
@Target(PARAMETER)
@Retention(RUNTIME)
public @interface IpAddress {
}
이 어노테이션도 인터페이스에는 아무것도 없다
public class IpAddressArgumentResolver implements HandlerMethodArgumentResolver {
private final NetworkContext networkContext;
@Override
public boolean supportsParameter(MethodParameter parameter) {
return parameter.hasParameterAnnotation(IpAddress.class) && parameter.getParameterType().equals(String.class);
}
@Override
public Object resolveArgument(MethodParameter parameter, ModelAndViewContainer mavContainer,
NativeWebRequest webRequest, WebDataBinderFactory binderFactory) {
return networkContext.getIpAddress();
}
}
Argument Resolver
를 봐도 그냥 ip
만 가져오는 것을 볼 수 있다
@Auth
의 동작방식에서 로직을 뺀 것 같다
정리를 해보자면 @Auth
어노테이션이 달려있는 파라미터를 가져와서 null
검사를 거쳐서 (일종의 인증 로직) 다시 id
를 반환해주는 것을 볼 수 있다
@IpAddress
는 어노테이션이 달려있는 파라미터의 ip
를 가져오는 것으로 볼 수 있다!