문제

Spring Boot + thymeleaf으로 진행하던 프로젝트를 테스트 하던 중
Ajax로 호출하는 Methoed를 Test 하였더니 아래와 같이 403 error가 return 되면서 Test가 실패했다.


원인

thymeleaf를 사용하면 기본적으로 CSRF 토큰을 넘겨주기 때문에 따로 csrf 토큰을 생성하지 않았으나, ajax에선 직접 csrf 토큰을 생성해서 넘겨줘야 하기 때문에 csrf 토큰을 생성해서 넘겨주도록 되어 있는데 Test 시에는 그렇지 않아서 403 Forbidden가 발생한 것으로 보인다.

403

thymeleaf config.html

1
2
<meta id="_csrf" name="_csrf" th:content="${_csrf.token}"/>
<meta id="_csrf_header" name="_csrf_header" th:content="${_csrf.headerName}"/>

common.js

1
2
const token = $("meta[name='_csrf']").attr("content");
const header = $("meta[name='_csrf_header']").attr("content");

해결

mockMvc.perform에서 get, post 호출 시 SecurityMockMvcRequestPostProcessors 의 csrf()with()로 파라미터에 추가한다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
import static org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestPostProcessors.csrf;

...

//when
ResultActions action = mockMvc.perform(post("/word/add")
.param("save", "")
.content(addwordToJson)
.contentType(MediaType.APPLICATION_JSON)
.with(csrf())) // 이 부분
.andDo(print());

...

추가 한 후 다시 테스트 해보면 파라미터에 csrf 토큰이 생성되어 정상적으로 동작하는 것을 확인 할 수 있다.

1
2
3
4
5
6
7
8
9
10
MockHttpServletRequest:
HTTP Method = POST
Request URI = /word/add
Parameters = {save=[], _csrf=[6757e021-c0a3-4390-81d9-9c917d89c8c1]} // 이 부분
Headers = [Content-Type:"application/json", Content-Length:"59"]
Body = <no character encoding set>
Session Attrs = {SPRING_SECURITY_CONTEXT=org.springframework.security.core.context.SecurityContextImpl@83a38fdf: Authentication: org.springframework.security.authentication.UsernamePasswordAuthenticationToken@83a38fdf: Principal: org.springframework.security.core.userdetails.User@31bf3c: Username: jgji; Password: [PROTECTED]; Enabled: true; AccountNonExpired: true; credentialsNonExpired: true; AccountNonLocked: true; Granted Authorities: ROLE_USER; Credentials: [PROTECTED]; Authenticated: true; Details: null; Granted Authorities: ROLE_USER}

...

성공


참고 사이트


전체 코드