본문 바로가기

STUDY/Spring

Spring Boot | HandlerMethodArgumentResolver로 Authentication 정보 간단하게 받기

Spring Security를 사용하면 인증된 사용자의 정보를 Authentication 객체를 통해 받을 수 있다.
AuthenticationSecurityContextHolder에 저장되어 있음

HandlerMethodArgumentResolver

HandlerMethodArgumentResolver 인터페이스는 두 개의 메서드를 구현하도록 하고 있다.

  • supportsParameter(): 주어진 메서드 파라미터를 이 reolver로 지원할지 여부
  • resolveArgument(): 파라미터에 전달할 객체를 생성
boolean    supportsParameter(MethodParameter parameter)  

Object    resolveArgument(MethodParameter parameter, 
                        ModelAndViewContainer mavContainer, 
                        NativeWebRequest webRequest, 
                        WebDataBinderFactory binderFactory)


Authentication의 값(username, details)를 계속 사용해야 하는데 해당 코드가 반복되므로 HandlerMethodArgumentResolver를 통해 간단히 애노테이션만으로 처리하면 좋을 것 같았다!

1. 애노테이션 만들기

@AuthUser라는 애노테이션을 만든다.
@Target(ElementType.PARAMETER)는 파라미터에 해당 애노테이션을 사용할 수 있다는 뜻이다. 여러 타겟을 입력할 수 있다.
애노테이션을 만들 때는 클래스 선언부에 class 대신 @interface를 사용한다.

@Target(ElementType.PARAMETER)
@Retention(RetentionPolicy.RUNTIME)
public @interface AuthUser {
}

2. HandlerMethodArgumentResolver 구현체 작성

HandlerMethodArgumentResolver를 상속받는 구현체를 작성한다.
위에서 알아본 두 개의 메서드를 오버라이딩하여 원하는 값을 바인딩 하면 된다!

@Component
public class AuthenticationArgumentResolver implements HandlerMethodArgumentResolver {

    @Override
    public boolean supportsParameter(MethodParameter parameter) {
        return false;
    }

    @Override
    public Object resolveArgument(MethodParameter parameter, ModelAndViewContainer mavContainer, NativeWebRequest webRequest, WebDataBinderFactory binderFactory) throws Exception {
        return null;
    }

}

supportsParameter()에서는 해당 파라미터에 위에서 만든 @AuthUser애노테이션이 사용됐는지, 그리고 해당 파라미터가 RegisterDto클래스가 맞는지 확인한다.

@Override
public boolean supportsParameter(MethodParameter parameter) {
    final boolean isRegUserAnnotation = parameter.getParameterAnnotation(AuthUser.class) != null;
    final boolean isRegisterDto = parameter.getParameterType().equals(RegisterDto.class);
    return isRegUserAnnotation && isRegisterDto;
}

supportsParameter()의 반환 값이 true라면 해당 메서드가 실행된다.
SecurityContextHolder에 저장된 Authentication을 얻어 원하는 값들을 정제하고 new RegisterDto()에 바인딩하여 반환하도록 작성했다. 이제 메서드의 파라미터에 someMethod(@AuthUser RegisterDto registerDto)이런식으로 작성하면 간단하게 원하는 값을 얻을 수 있게된다.

@Override
public Object resolveArgument(MethodParameter parameter, ModelAndViewContainer mavContainer, NativeWebRequest webRequest, WebDataBinderFactory binderFactory) throws Exception {

    final Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
    if (authentication != null) {

        // 생략..

        return new RegisterDto(/* 생략.. */);
    }
    return null;
}

3. argumentResolver 등록

스프링 부트에서 해당 resolver를 인식할 수 있도록 등록 한다.
HandlerMethodArgumentResolver는 항상 WebMvcConfiguereraddArgumentResolvers()를 통해 추가해야 한다.

@RequiredArgsConstructor
@Configuration
public class WebConfig implements WebMvcConfigurer {

    private final AuthenticationArgumentResolver authenticationArgumentResolver;

    @Override
    public void addArgumentResolvers(List<HandlerMethodArgumentResolver> resolvers) {
        resolvers.add(authenticationArgumentResolver);
    }
}

사용해보기!

이렇게 메서드의 파라미터에 @AuthUser 애노테이션을 붙이기만 하면 해당 파라미터에 데이터가 바인딩된다.

@PostMapping()
public Response (@AuthUser RegisterDto registerDto,
                 @RequestBody SaveDto saveDto) {
   // 생략
}




참고

  • 이동욱 저 스프링 부트와 AWS로 혼자 구현하는 웹 서비스 195p~199p