인증 필터를 적용한 뒤에는 SecurityContext를 통해 유저의 정보를 읽어온다. 따라서 파라미터를 통해 유저의 정보를 전달받지 않아도 유저별로 자신의 정보를 요청할 수 있게 된다.
흔히 사용하는 애너테이션으로 @AutenticationPrincipal은 시큐리티 컨텍스트의 Authentication의 Principal을 매개변수로 전달한다.
인증 필터를 구현하는 과정에서 CustomUserDetails를 UsernamePasswordAuthenticationToken의 Principal 필드로 주입했다.
따라서, CustomUserDetails를 이용해 개인 정보를 조회하는 컨트롤러 메서드들을 정의해뒀다.
단위 테스트 과정에서 이를 이용하기 위해
@WithMockUser 애너테이션을 이용해 유저 정보를 입력했다.
하지만, NullPointerException이 발생했고, @AuthenticationPrincipal로 주입받은 객체가 null임을 확인했다.
이 애너테이션을 통해 생성된 SecurityContext의 인증 객체를 출력해봤다.
org.springframework.security.core.userdetails.User [Username=wonjun@mail.com, Password=[PROTECTED], Enabled=true, AccountNonExpired=true, CredentialsNonExpired=true, AccountNonLocked=true, Granted Authorities=[ROLE_USER]]
내가 정의한 CustomUserDetails가 아니라, security.core.userdetails.User가 생성되었다.
이 때문에 발생한 문제임을 알았다.
위 애너테이션을 제거하고, @BeforeEach 메서드를 통해 직접 시큐리티 컨텍스트를 주입해봤다.
@BeforeEach
public void setup() {
Authentication auth = new UsernamePasswordAuthenticationToken(
new CustomUserDetails(
User.builder().email(TEST_USER_EMAIL).id(TEST_USER_ID).role("USER").build()),
null,
List.of(new SimpleGrantedAuthority("USER")));
SecurityContextHolder.getContext().setAuthentication(auth);
}
위와 같이 직접 SecurityContext에 주입해주니 성공적으로 테스트가 통과되었다.
컨트롤러 테스트들에 모두 적용하여 테스트를 성공시켰다.
시큐리티가 포함된 프로젝트에서 컨트롤러 단의 테스트는 Mock Authentication을 추가해야 함을 잊지 말아야겠다.