Spring Security 프레임워크가 적용된 상태일때 쓰는 테스트용 어노테이션
이다.
Spring Security 에서 인증된 사용자를 Mock으로 만들어서 테스트를 수행
할 수 있도록 한다.
상황을 가정해보자.
Spring Security 를 이용해서 프로그램을 돌리는 상황이다.
API 호출할 때 Controller 로 들어오기 전에 Spring Security 필터를 거쳐서 들어오게 된다.
하지만, 테스트 코드를 실행할 때는 인증정보가 들어오지 않는다.
그러면 아무리 API 를 호출해도 인증정보가 없다는 것
만 나온다.
구체적으로 이야기하면, 302 응답
이 나온다.
인증을 위해서 리다이렉션이 필요하다는 의미다.
Spring Security 에서 permitAll() 로 설정하지 않은 URL 을 제외하고는 모두 테스트 실패가 나온다.
프로그램을 3티어로 구성했다고 하자.
Controller - Service - Repository 이렇게 구성한거다.
이전 예에서는 Controller 에서 문제가 발생한다.
API 호출하는 부분에서 오류가 발생하기 때문.
지금 상황은 Service 레이어에서 SecurityContextHolder.getContext()
를 사용하는 경우다.
테스트 코드에서는 Spring Security 를 거치지 않았기 때문에 SecurityContext 가 null 인 상황
이다.
이때 위 어노테이션을 활용해서 SecurityContext 를 Mock 으로 만들어주고 테스트를 돌리는 거다.
@WithMockUser
username
, password
, roles
을 Mock 으로 넣어줌UserDetails 객체를 만들어주는 것
SecurityContext
에도 올라간다고 한다.직접 만든 Authentication 인증정보는 사용 불가
@Test
@WithMockUser(value = "danaka@naver.com", roles = {"USER"})
void test() {
// 테스트 코드
}
UserDetails 객체를 조회
해서 SecurityContext
를 만든다는 점value
: 지정한 사용자 이름userDetailsServiceBeanName
UserDetailsService 구현체
@Test
@WithUserDetails(value = "danaka@naver.com")
void test() {
// 테스트 코드
}
Authentication 을 커스텀한 경우에 사용
한다.커스텀 어노테이션
작성SecurityContextFactory
를 구현한다.메소드에 어노테이션
을 달아준다.@Test
@WithMockCustomUser
void test() {
// 테스트 코드
}
public class WithMockCustomUserSecurityContextFactory implements
WithSecurityContextFactory<WithMockCustomUser> {
@Override
public SecurityContext createSecurityContext(WithMockCustomUser mockCustomUser) {
SecurityContext context = SecurityContextHolder.createEmptyContext();
JwtAuthenticationToken authentication =
new JwtAuthenticationToken(
new JwtAuthenticationPrincipal(
mockCustomUser.email()),
null,
List.of(new SimpleGrantedAuthority(mockCustomUser.role())));
context.setAuthentication(authentication);
return context;
}
}
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
@WithSecurityContext(factory = WithMockCustomUserSecurityContextFactory.class)
public @interface WithMockCustomUser {
String email() default "danaka@naver.com";
String role() default "USER";
}
인증정보를 나만 쓰는게 아니라, 팀원들도 같이 쓰도록 구현하느라 Spring Security 테스트 하는게 꽤 큰 문제가 됐다.
로직에는 인증정보를 사용하도록 했는데, 테스트가 안 되는거니까…
그래도 키워드 잘 얻어서 얼른 찾아보고 적용할 수 있어서 다행이다.
만들어둔 @WithSecurityContext
는 잘 가지고 다니면서 나중에 써먹어야 겠다.
아 참고로, 나는 위 세 개 테스트 어노테이션 중에 마지막 @WithSecurityContext
를 사용했다.
JWT
랑 OAuth2
를 사용하면서 Authentication 을 커스텀
해서 사용했기 때문.
이 문제 해결하면서, 원래 UserDetails 안 쓰는 방식으로 구현했던 Security 코드로 리팩토링할 수 있어서 좋았다.