
프로젝트에서 Spring Security와 JWT를 적용 중이다.
MockMvc로 API 테스트 시, 사용자 인증이 안되어 401 에러가 발생하였다.
/* Mock 사용자를 만드는 어노테이션 */
@Retention(RetentionPolicy.RUNTIME)
@WithSecurityContext(factory = WithMockCustomUserSecurityContextFactory.class)
public @interface WithMockCustomUser {
String userName() default "1";
String roleType() default "RULE_USER";
}
/* JWT Filter와 비슷한 로직의 Mock Filter */
public class WithMockCustomUserSecurityContextFactory implements WithSecurityContextFactory<WithMockCustomUser> {
@Override
public SecurityContext createSecurityContext(WithMockCustomUser annotation) {
final SecurityContext securityContext = SecurityContextHolder.createEmptyContext();
final Collection<GrantedAuthority> authorities = List.of(new SimpleGrantedAuthority(annotation.roleType()));
final Authentication authentication = new UsernamePasswordAuthenticationToken(annotation.userName(), "", authorities);
securityContext.setAuthentication(authentication);
return securityContext;
}
}
가짜 유저의 Authentication 객체를 새로 만든 Security Context에 저장되도록 했다.
가짜 인증을 통해 DB를 사용하지 않아도 됨으로 간편하게 테스트 가능하다.

위 코드처럼 단위 테스트에 @WithMockCustomUser 어노테이션을 추가하면 401 에러를 우회할 수 있다.

401 에러가 해결된 후에 403 에러가 발생하였다.
401로 사용자가 인증은 되었지만 해당 API에 대한 권한이 없기 때문에 발생한 것이다.
이는 Spring Security가 기본적으로 CSRF 공격으로부터 방어하도록 설정되어 있기 때문이다.
그래서 CSRF Token이 없으면 권한이 없다고 판단하게 되는 것이다.
ResultActions result = mockMvc.perform(
post("/api/login/naver")
.with(csrf()) // CSRF Token 추가
.content(objectMapper.writeValueAsString(request))
.contentType(MediaType.APPLICATION_JSON)
);
테스트 요청을 보내는 코드에 .with(csrf())를 추가하여 CSRF Token이 포함되어 요청하도록 테스트 한다.