WORK/STUDY

[Spring Security] 테스트에 시큐리티 적용하기

Justin Mendes 2023. 10. 28. 14:14
 

스프링 부트와 AWS로 혼자 구현하는 웹 서비스 - 예스24

가장 빠르고 쉽게 웹 서비스의 모든 과정을 경험한다. 경험이 실력이 되는 순간!이 책은 제목 그대로 스프링 부트와 AWS로 웹 서비스를 구현한다. JPA와 JUnit 테스트, 그레이들, 머스테치, 스프링

www.yes24.com

*위의 책을 따라 학습한 것을 정리한 내용입니다


이전에는 테스트 코드를 작성하고 API를 바로 호출하는 형태였다.

하지만 시큐리티가 활성화되면서 인증된 사용자가 아니면 API를 호출할 수 없기 때문에 테스트 코드에 인증한 사용자가 호출한 것처럼 작동하도록 수정을 해야한다.

 

※Test에 application.propertise가 없으면 main 설정을 그대로 가져온다. (application.propertise만 가져오고 oauth는 x)

 

테스트를 위한 application.propertise를 만든다.

src/test/resources에 application.properties를 생성한다.

spring.jpa.show_sql=true
spring.jpa.properties.hibernate.dialect=org.hibernate.dialect.MySQL5InnoDBDialect
spring.h2.console.enabled=true
spring.mvc.hiddenmethod.filter.enabled=true
spring.session.store-type=jdbc

#Test OAuth
spring.security.oauth2.client.registration.google.client-id=test
spring.security.oauth2.client.registration.google.client-secret=test
spring.security.oauth2.client.registration.google.scope=profile, email

한가지 문제가 있다. 302FOUND Error가 뜬다는 것이다. 임시방편으로 만든 cient 정보가 인증되지 않았기 때문이다.

test에 관련된 의존성을 추가해주자.

 

build.gradle에 다음 코드를 추가한다.

testCompile("org.springframework.security:spring-security-test")

 

PostsApiControllerTest의 두 메소드에 임의 사용자 인증을 추가한다.

@Test
@WithMockUser(roles="USER")
public void Posts_등록() throws Exception {
	...
}
	
@Test
@WithMockUser(roles="USER")
public void Posts_업데이트() throws Exception {
	...	
}

 

@WithMockUser(roles="USER")

- 인증된 모의 사용자를 만들어서 사용

- roles에 권한 추가 가능

- MockMvc에서만 작동

 

@SpringbootTest에서 MockMvc를 사용하는 방법

PostsApiControllerTest 수정

@RunWith(SpringRunner.class)
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
public class PostsApiControllerTest {
	...
	@Autowired
	private WebApplicationContext context;
	
	private MockMvc mvc;
	
	@Before
	public void setup() {
		mvc = MockMvcBuilders
				.webAppContextSetup(context)
				.apply(springSecurity())
				.build();
	}
	...
    
	@Test
	@WithMockUser(roles="USER")
	public void Posts_등록() throws Exception {
		//given
		...
		
		//when
		mvc.perform(post(url)
				.contentType(MediaType.APPLICATION_JSON_UTF8)
				.content(new ObjectMapper().writeValueAsString(requestDto)))
				.andExpect(status().isOk());
                
		//then
		List<Posts> all = postsRepository.findAll();
		assertThat(all.get(0).getTitle()).isEqualTo(title);
		assertThat(all.get(0).getContent()).isEqualTo(content);
		
	}
	
	@Test
	@WithMockUser(roles="USER")
	public void Posts_업데이트() throws Exception {
		//given
		...
		
		//when
		mvc.perform(post(url)
				.contentType(MediaType.APPLICATION_JSON_UTF8)
				.content(new ObjectMapper().writeValueAsString(requestDto)))
				.andExpect(status().isOk());
		
		//then
		List<Posts> all = postsRepository.findAll();
		assertThat(all.get(0).getTitle()).isEqualTo(expectedTitle);
		assertThat(all.get(0).getContent()).isEqualTo(expectedContent);
		
	}
}

 

 

HelloControllerTest

@RunWith(SpringRunner.class)
@WebMvcTest(controllers = HelloController.class,
	excludeFilters = {
		@ComponentScan.Filter(type = FilterType.ASSIGNABLE_TYPE, classes = SecurityConfig.class)
	})
public class HelloControllerTest {
	@Autowired
	private MockMvc mvc;
	
	@WithMockUser(roles="USER")
	@Test
	public void hello_리턴() throws Exception {
		...
	}
	
	@WithMockUser(roles="USER")
	@Test
	public void helloDto_리턴() throws Exception {
		...
	}
}

위의 테스트를 진행하면서 알아야 할 게 있다.

 

@WebMvcTest

- 읽는 것: WebSecurityConfigurerAdapter, WebMvcConfigurer,  @ControllerAdvice, @Controller

- 읽지 않는 것: @Repository, @Service, @Component, ...

 

위의 테스트는 SecurityConfig를 생성하기 위해 필요한 CustomOAuth2UserService를 읽을 수 없다.

 

일단 스캔 대상에서 SecurityConfig를 제외하고,

@WithMockUser를 통해 가짜 인증 사용자를 생성한다.

 

실행하면 java.lang.illegalStateException:Failed to load ApplicationContext가 뜬다.

Application.java를 보면 @EnableJpaAuditing 어노테이션이 있다.

이를 사용하기 위해선 최소 하나의 @Entity 클래스가 필요하다.

하지만 위 코드에서는 @WebMvcTest이다보니 당연히 Entity가 없다.그래서 Application과 @EnableJpaAuditing을 분리해보겠다.

 

일단 Application에서 @EnableJapAuditing 어노테이션을 제거한다.

config폴더에 JpaConfig 클래스를 생성한다.

@Configuration
@EnableJpaAuditing
public class JpaConfig { }

다시 테스트를 수행하면 무사히 수행되는 것을 확인할 수 있다.