Back-End/Spring

[Spring] Security 테스트 적용기 02 (+ Oauth2.0, jwt)

COBI-98 2023. 10. 14. 22:17

Spring Security 테스트 적용기 02

OAuth 2.0 또는 JWT와 같은 보다 복잡한 인증 메커니즘을 사용하는 경우,

@WithMockUser와 @WithUserDetails만으로는 충분하지 않을 수 있다.

 

OAuth 2.0 및 JWT와 같은 토큰 기반 인증 방식은 다른 방식으로 테스트해야 한다.

 

일반적인 방식으로 Spring Security Test 프레임워크에서 지원하는 

@WithSecurityContext 애노테이션을 사용하여,

커스텀한 @WithMockUser 애노테이션을 만들고 OAuth 2.0 인증을 가정하는 테스트를 작성할 수 있다.

 

테스트 코드 작성에 도움을 주는 애노테이션들을 정리하고,

@WithSecurityContext 어노테이션을 사용하여 @WithMockOAuth2User 애노테이션을 만들어 보려고한다.

 

@AutoConfigureMockMvc

OAuth 2.0 보안을 테스트하기 위해 MockMvc를 설정하는 데 사용되는 애노테이션이다.

MockMvc를 자동으로 구성하고 OAuth 2.0 클라이언트 자격 증명을 처리하는 데 도움을 준다.

@SpringBootTest
@AutoConfigureMockMvc
public class CustomOAuth2UserControllerTest {
    // 테스트 코드 작성
}

테스트를 진행할 때 @AuthConfigureMockMvc와 @AuthConfigureRestDocs 가 존재하는데,

MockMvc는 Spring Security를 지원하며, 주입된 사용자 정보를 쉽게 사용할 수 있기 때문에

실제 요청과 좀 더 유사한 MockMvc를 이용하기로 하였다.

 

@WithMockOauth2User

WithMockOauth2User interface

@WithSecurityContext 어노테이션을 사용하면 사용할 SecurityContext를 지정해 줄 수 있다.

즉 어떤 객체로 바인딩할 것인지를 직접 만들어서 적용할 수 있다.

 

WithMockOAuth2UserSecurityContextFactory

WithMockOAuth2UserSecurityContextFactory.java

사용할 SecurityContext를 정의해 줄 클래스이다.

  1. SecurityContext 객체를 생성
  2. UserPrincipal에 모의 소셜로그인 사용자를 정의한다.
  3. Authentication 객체에 유저정보, 비밀번호, 권한 정보를 설정한다.
  4. SecurityContext는 생성한 Authentication을 설정하고, 이 컨텍스트를 반환한다.

 

이후 mockmvc 테스트를 진행할 때,

MockMvcBuilders.webAppContextSetup(context)에 이 사용자 정보를 주입하게 된다.

 

AcceptanceTest

AcceptanceTest.java

SecurityContext는 생성한 Authentication을 설정하고, 이 콘텍스트를 반환하기 때문에

 

테스트 메서드에서 @WithMockOAuth2User 어노테이션을 사용하면,

해당 사용자 정보로 보안 컨텍스트가 설정되어 테스트가 실행되는 구조이다.

 

@WithMockOauth2User를 활용한 사용자 이름 변경 테스트

@Test
@DisplayName("사용자 이름 변경")
@WithMockOAuth2User
void modifyName() throws Exception {

    // given
    UserNameRequestDto dto = UserNameRequestDto.builder()
            .name("김철수").build();

    // when
    ResultActions result = mockMvc.perform(put("/api/oauth2/user/name")
            .header(HttpHeaders.AUTHORIZATION, BEARER_TYPE + "ACCESS_TOKEN")
            .content(objectMapper.writeValueAsString(dto))
            .contentType(MediaType.APPLICATION_JSON)
    );

    // then
    result.andExpect(status().isOk())
            .andDo(document(DEFAULT_RESTDOC_PATH,
                    requestHeaders(
                            headerWithName(HttpHeaders.AUTHORIZATION).description("액세스 토큰")
                    ),
                    requestFields(
                            fieldWithPath("name").type(JsonFieldType.STRING).description("사용자 이름")
                    ),
                    responseFields(
                            fieldWithPath("status").type(JsonFieldType.NUMBER).description("응답 상태 코드"),
                            fieldWithPath("message").type(JsonFieldType.STRING).description("응답 메시지"),
                            fieldWithPath("data").type(JsonFieldType.STRING).description("응답 데이터")
                    )
            ));
}

header에는 jwt 토큰 예시,

@WithMockOauth2User와의 유저정보와

dto의 이름 정보가 잘 적용된 것을 확인할 수 있다.

 

정리

이러한 도구와 커스텀한 애노테이션을 사용하여

Spring Security OAuth를 테스트하면 보안 및 인증 관련 시나리오를 다룰 수 있다.

 

특정 시나리오를 시뮬레이션하고 필요한 인증 및 권한을 설정하는 데 도움 되어 쉽게 테스트 코드를 작성할 수 있었고,

Security의 구조 또한 다시 알아볼 수 있는 경험이 된 것 같다.

 

reference

Spring docs, Testing OAuth 2.0