@WebMvcTest 401/403 에러 처리하기
API 개발 후 테스트를 하는 도중에 발생한 에러를 처리하는 방법에 대해 알아보겠습니다.
테스트 코드
@WebMvcTest(AuthController.class)
class AuthControllerTest {
@MockBean
private AuthService authService;
@MockBean
private MailService mailService;
@MockBean
private TokenProvider tokenProvider;
@Autowired
private MockMvc mockMvc;
@Autowired
private ObjectMapper objectMapper;
@Test
@DisplayName("회원가입 성공")
void success_signUp() throws Exception {
//given
SignUpDto user = SignUpDto.builder()
.email("test@test.com")
.password("12345678")
.nickname("김철수")
.phoneNumber("010-1234-5678")
.build();
given(authService.signUp(any())).willReturn(user);
//when
//then
mockMvc.perform(post("/api/auth/signUp")
.contentType(MediaType.APPLICATION_JSON)
.content(objectMapper.writeValueAsString(user))
.andExpect(status().isCreated())
.andExpect(jsonPath("$.email").value("test@test.com"))
.andExpect(jsonPath("$.password").value("12345678"))
.andDo(print());
}
컨트롤러 코드
@PostMapping("/signUp")
public ResponseEntity<?> signUpMember(@RequestBody SignUpDto request) {
return ResponseEntity.status(HttpStatus.CREATED).body(memberService.signUp(request));
}
테스트 시에 발생한 에러 (403)
에러 원인
- 스프링 시큐리티의 CSRF 로 인한 에러
![[Mockito] @WebMvcTest 401/403 에러 처리하기 - @WebMvcTest 401/403 에러 처리하기 - 테스트 시에 발생한 에러 (403) - [Mockito] @WebMvcTest 401/403 에러 처리하기 - @WebMvcTest 401/403 에러 처리하기 - 테스트 시에 발생한 에러 (403) -](http://t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png)
에러 이유 및 해결방법
![[Mockito] @WebMvcTest 401/403 에러 처리하기 - @WebMvcTest 401/403 에러 처리하기 - 테스트 시에 발생한 에러 (403) - 에러 이유 및 해결방법 [Mockito] @WebMvcTest 401/403 에러 처리하기 - @WebMvcTest 401/403 에러 처리하기 - 테스트 시에 발생한 에러 (403) - 에러 이유 및 해결방법](http://t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png)
# CSRF(Cross Site Request Forgery)
- 공격자가 악의적인 코드를 심어놓은 사이트를 만들고, 로그인 된 사용자가 클릭하게 만들어서 사용자의 의지와 무관한 요청을 발생시키는 공격
- 사용자는 로그인한 상태이고 쿠키, 권한을 갖고 있기 때문에 공격자가 위조한 웹사이트에 방문하게 되면 사용자가 모르게 악의적인 POST, DELETE 요청을 정상 수행하도록 만들어버리는 공격
- 이로 인해 스프링 시큐리티에서는 "CSRF 토큰" 을 이용해 토큰 값을 비교해서 일하는 경우에만 메서드를 만들도록 처리한다. (Synchronizer Token Pattern)
- 즉, 스프링 시큐리티가 적용되면서 CSRF 공격에 대한 방어가 활성화 된 것이고, 이로 인해 IDE에서 진행한 테스트 과정을 다른 사이트의 요청으로 인지한 것이다.
# Synchronizer Token Pattern
- 서버가 뷰를 만들어줄 때 사용자 별로 랜덤값을 만들어 세션에 저장한 다음 이를 뷰 페이지에 같이 담아 넘겨주게 된다.
- 클라이언트는 HTTP 요청마다 숨겨진 csrf 토큰을 같이 넘겨줘야하는 방식이다.
- 서버는 HTTP Request에 있는 csrf 토큰값과 세션에 저장되어 있는 토큰값을 비교해 일치하는 경우에만 처리를 진행한다
- 위조된 사이트의 경우 csrf 토큰값이 일치하지 않기 때문에 공격자가 악의적인 코드를 심어놔도 이를 실행하지 않는다.
* GET 요청에 대해서는 csrf 검증을 수행하지 않는다.
# with(csrf())를 추가한 경우
- 파라미터로 _csrf 값을 보내주는 것을 확인할 수 있다.
![[Mockito] @WebMvcTest 401/403 에러 처리하기 - @WebMvcTest 401/403 에러 처리하기 - 테스트 시에 발생한 에러 (403) - 에러 이유 및 해결방법 [Mockito] @WebMvcTest 401/403 에러 처리하기 - @WebMvcTest 401/403 에러 처리하기 - 테스트 시에 발생한 에러 (403) - 에러 이유 및 해결방법](https://blog.kakaocdn.net/dn/29vjm/btsCgqV3bxq/tbdh1bS3an8Y12E2xmmNtK/img.png)
# with(csrf())를 사용하지 않은 경우
- 세션에 저장된 CSRF 값과 매칭되지 않음
- 요청을 수행하지 못하고 403 에러 반환
![[Mockito] @WebMvcTest 401/403 에러 처리하기 - @WebMvcTest 401/403 에러 처리하기 - 테스트 시에 발생한 에러 (403) - 에러 이유 및 해결방법 [Mockito] @WebMvcTest 401/403 에러 처리하기 - @WebMvcTest 401/403 에러 처리하기 - 테스트 시에 발생한 에러 (403) - 에러 이유 및 해결방법](https://blog.kakaocdn.net/dn/UgLiO/btsCfy8cPJE/QtYHuOB8DaIReKbhSKhkM1/img.png)
# 프로젝트에서 csrf 기능을 disable 한 이유?
- csrf 토큰 방식은 각 사용자에 대한 세션을 이용하는 방식이다.
- 웹 브라우저를 통한 접근의 경우, 세션/쿠키를 사용해 상태를 유지하려고 하는 경우 csrf를 사용하는 것이 안전한다.
- REST API의 경우 보통 무상태성을 유지하며 JWT와 같은 코근 방식으로 인증하게 되면 요청이 세션에 의존하지 않기 때문이다.
* Thymeleat와 같은 템플릿 엔진을 통해 View를 제공하는 웹/앱 어플리케이션의 경우 -> csrf 사용 권장
* REST API 제공하는 애플리케이션 -> csrf 사용하지 않아도 무방
테스트 시에 발생한 에러 (401)
에러 원인
- API 권한 미설정
![[Mockito] @WebMvcTest 401/403 에러 처리하기 - @WebMvcTest 401/403 에러 처리하기 - 테스트 시에 발생한 에러 (401) - 에러 원인 [Mockito] @WebMvcTest 401/403 에러 처리하기 - @WebMvcTest 401/403 에러 처리하기 - 테스트 시에 발생한 에러 (401) - 에러 원인](https://blog.kakaocdn.net/dn/bTaro4/btsCgvb15p4/UjOKwReIVAtj5ATN9MctAk/img.png)
에러 이유 및 해결방법
# @SpringBootTest vs @WebMvcTest
- SpringBootTest의 경우 어노테이션(@Controller, @Service @Bean) 등 전부 불러와서 실제 환경과 동일하게 테스트 할 때 사용한다.
- @WebMvcTest의 경우에는 절반 가량의 스프링 빈만 불러와서 사용한다.
- 대신에, @MockBean으로 주입해줄 경우 스프링 빈으로 저장할 수 있다.
# SpringBootWebSecurityConfiguration
- 스프링 시큐리티는 맨 처음 적용하면 페이지에 들어가면 바로 로그인 창이 뜨게 되어있다.
- 모든 요청에 대해 아무 권한이나 갖고있으면 되도록 설정되어 있다.
- 테스트를 위해 매핑 신호를 날리면 스프링 시큐리티로 인해 401 Unauthorized 에러를 반환한다.
- 모든 요청에 대해 권한이 필요하도록 기본적으로 설정되어 있기 때문이다.
# 설정 방법
1. 어노테이션을 활용하여 권한을 넘겨주는 방법
![[Mockito] @WebMvcTest 401/403 에러 처리하기 - @WebMvcTest 401/403 에러 처리하기 - 테스트 시에 발생한 에러 (401) - 에러 이유 및 해결방법 [Mockito] @WebMvcTest 401/403 에러 처리하기 - @WebMvcTest 401/403 에러 처리하기 - 테스트 시에 발생한 에러 (401) - 에러 이유 및 해결방법](https://blog.kakaocdn.net/dn/dqmNuB/btsCksSRbQV/mKmRRBT0P9L8gp9x4rFWH0/img.png)
- @WithMockUser
- @WithAnonymousUser - 미인증 사용자
- @WIthUserDetails - 메서드가 principal 내부의 값을 직접 사용하는 경우(별도의 사전 설정이 필요하다)
2. MockMvcBuilder이용하여 MockMvc 초기화
![[Mockito] @WebMvcTest 401/403 에러 처리하기 - @WebMvcTest 401/403 에러 처리하기 - 테스트 시에 발생한 에러 (401) - 에러 이유 및 해결방법 [Mockito] @WebMvcTest 401/403 에러 처리하기 - @WebMvcTest 401/403 에러 처리하기 - 테스트 시에 발생한 에러 (401) - 에러 이유 및 해결방법](https://blog.kakaocdn.net/dn/bC0A3F/btsCizSw2CW/Qhiwk1wcb3LTlVsq1lN9RK/img.png)
- MockMvc를 @Autowired 를 하지 않고 테스트 초기에 초기화를 하는 작업이다.
- @BeforeEach 어노테이션을 이용해서 테스트 메서드가 실행되기 전에 수행되는 어노테이션으로 테스트를 진행할 때마다 mockMvcBuilder를 이용해서 Mock에 독립적인 클래스(controller)를 선언하고 인스턴스를 생성한다.
참고
https://iseunghan.tistory.com/302
https://sedangdang.tistory.com/303
https://adjh54.tistory.com/347#1.%20Mock%20%EC%B4%88%EA%B8%B0%ED%99%94-1
'TEST > JUnit' 카테고리의 다른 글
단위 테스트 방법 [part 3 : 리포지토리(Repository)] (0) | 2023.11.08 |
---|---|
단위 테스트 방법 [part 2 : 서비스(Service)] (0) | 2023.11.08 |
단위 테스트 방법 [part 1 : 컨트롤러(Controller)] (0) | 2023.11.08 |
댓글