새소식

Debug

[Spring] Test시, MockMvc 요청의 Response가 403Error가 날때

  • -

Test시, MockMvc 요청의 403Error

@ExtendWith(SpringExtension.class)
@SpringBootTest
@AutoConfigureMockMvc
public class LoginServiceTest {

    private static final Logger log = LoggerFactory.getLogger(CharacterServiceTest.class);

    @Autowired
     MockMvc mockMvc;

    @Autowired
     ObjectMapper objectMapper;

    @Test
    void InvalidPassword__sholud_pass_with_error_result() throws Exception {
        LoginInpVo inpVo = new LoginInpVo();
        inpVo.setEmail("donghyeondev@gmail.com");
        inpVo.setPassword("test123");

        mockMvc.perform(post("/login")
                .header("Origin","*")
                .contentType(MediaType.APPLICATION_JSON)
                .content(objectMapper.writeValueAsString(inpVo))
                )
                .andDo(print());
    }

이와 같이 메서드 단위테스트를 진행했다.
원래대로라면 정상적인 통신을 하여 ErrorResult를 뱉어야했다.
하지만 결과는 403에러였다.

MockHttpServletResponse:
           Status = 403
    Error message = null
          Headers = [Content-Type:"text/plain"]
     Content type = text/plain
             Body = 
    Forwarded URL = null
   Redirected URL = null
          Cookies = []

생각되는 에러요인은 아래와 같다.

  1. Spring-Security 의존성을 선언하고 아무런 설정도 없을때
    ⇒ 이 경우는 나는 아니였다.
  2. ApplicationContext에 Filter가 들어가있는데 잘못되있을때
    ⇒ 이 경우는 @AutoConfigureMockMvcaddFilter 속성을 False 로 주면 해결가능

CORS FIlter에서 Origin을 * 로 처리하고 호출하면 정상적이긴 하다.

@Override
public void addCorsMappings(CorsRegistry registry) {
  registry.addMapping("/**")
      .allowedMethods("*")
//    .allowedOrigins("http://localhost:3000", "http://localhost:3030", "http://localhost:5000", "http://host.docker.internal:9090");
        .allowedOrigin("*")
}

하지만 모든 origin에 대해서 접근을 허용한다는건 그만큼 보안이 약해진다는것을 의미하고, 다른 방법을 찾아야한다.
실질적으로 헤더에 Origin : * 와 같은 속성을 주는게 맞는가에 대한 고민이 생겼다.
당연하게도 Swagger에서 테스트를 했을때는 정확하게 Origin이 들어가 있었다.

cors필터에 .allowedOrigin() , mockMvc Header에 스프링부트 프로젝트의 Origin을 추가해주고 다시 테스트를 진행하였다.

@Override
public void addCorsMappings(CorsRegistry registry) {
  registry.addMapping("/**")
      .allowedMethods("*")
    .allowedOrigins("http://localhost:8880","http://localhost:3000", "http://localhost:3030", "http://localhost:5000", "http://host.docker.internal:9090");
}
mockMvc.perform(post("/login")
                .header("Origin","http://localhost:8880")
                .contentType(MediaType.APPLICATION_JSON)
                .content(objectMapper.writeValueAsString(inpVo))
                )
                .andDo(print());

결과는 예상과 같이 성공이였다.

하지만 다른 문제가 생기는데 바로 한글 인코딩이 안되는 문제였다.

그 이유는 Response의 Content-Type 이였는데, 스프링 부트 2.2.0 이후부터 Charset이 빠졌기 때문이다.(해당 프로젝트는 2.4.2를 사용중)

해결방법

// WebConfig에 빈을 추가해주던지
@Bean
public CharacterEncodingFilter characterEncodingFilter(){
    CharacterEncodingFilter characterEncodingFilter = new CharacterEncodingFilter();
    characterEncodingFilter.setEncoding("UTF-8");
    characterEncodingFilter.setForceEncoding(true);
    return characterEncodingFilter;
}

혹은 이 전역설정이 모든 서비스가 아닌 Test시에만 사용된다고 싶다면, 별도로 yaml을 생성하고

# 야믈로 추가.. 나는 야믈이 더 깔끔해서 야믈을 선택함.
server:
  port: 8880
  servlet:
    encoding:
      charset: UTF-8
      enabled: true
      force: true

# 아래방식은 deprecated 됬다고한다.
spring:
    http:
        encoing:

@TestPropertySource(locations = "classpath:/test-encode.yml) 을 사용하면 된다.

Contents

포스팅 주소를 복사했습니다

이 글이 도움이 되었다면 공감 부탁드립니다.