既存のテストにセキュリティを適用
10089 ワード
既存のテストでは,セキュリティの適用により問題となる部分がある.
既存
直ちにAPIを呼び出すことができ、テストコードを構成しても直ちにAPIを呼び出すことができる.
セキュリティオプションの有効化
認証されたユーザーのみがAPIを呼び出すことができます
そのため、既存のAPIテストコードには認証権限がないため、認証されたユーザが呼び出すように動作するようにテストコードごとに修正する必要がある.
をクリックして全体テストを行います.
ニンジンテストに失敗
カスタマーID 2カスタマーサービス が見つかりませんでした.
CustomAuthe 2 UserServiceの作成に必要なソーシャルログイン設定はありません.
=>src/mainは設定されていますが、src/testでは設定されていません.
元のテスト時のアプリケーション.propertiesは自動的にインポートされます.(テスト中ではなくmain中の場合)
ただし、他のアプリケーション-oauth.properties(ソーシャルログインに関連する設定値ファイル)などのファイルをインポートしないため、エラーが発生しました.
だから私はあなたに作ってあげます.テストなので実際のGoogle連動は行わないので仮定定値を登録します.
src/test/resources/application.properties 302 Status Code
200(正常応答)ではなく302です.
これは、スプリングセキュリティ設定が許可されていないユーザーの要求を移動するためです.
認証ユーザをランダムに追加し、APIのみをテストします.
build.gradeにspring-security-testを追加します.
次に、PostsApiControllerTestの2つのテスト方法にランダムユーザー認証を追加します.
仮想ロールは、認証された偽のユーザーを作成して使用し、ロールに権限を追加できます.
これは、ROLE USER権限を持つユーザがAPIを要求するのと同じ効果である.
これでもすべてのテストに合格できません.
@WithMockUserはMockMvcでのみ動作するためです.
現在PostsApiControllerTestは@SpringBootTestのみで、MockMvcは全く使用されていません.
PostsApiControllerTest @WebMvcTestクライアントIDが見つかりません2 UserService 1とは異なり@WebMvTestを使用します.
1番スプリングの安全設定は正常に動作していますが、@WebMvcTestはCustomOuth 2 UserServiceをスキャンしないため、問題が発生しています
@WebMvcTest@ControllerAddiceと@Controllerを読み込みます.WebSecurityコンフィギュレータアダプタとWebMvcConfigureが含まれます.つまり、@Repository、@Service、@Componentはスキャンターゲットではないため、セキュリティ構成が読み込まれました.ただし、セキュリティ構成の作成に必要なCustomOuth 2 UserServiceを読み込めないため、エラーが発生しました
質疑応答:スキャンターゲットからSecurityConfigを削除 HelloControllerTest 既存
変更
@WithMockUserが偽認証ユーザーを生成
でもそれは間違いだ
このエラーは、アプリケーションクラスの@EnableJpaAuditingによって発生します.
@EnableJpaAuditingには少なくとも1つの@Entityクラスが必要です.でも@WebMcTestなのでもちろんありません
@EnableJpaAuditingは@SpringBootApplicationと一緒なので@WebMcTestでもスキャンします.
だから@EnableJpaAuditingと@SpringBootApplicationを分けます.
Application.Javaから構文を削除する
コンフィグパッケージにJpaConfig->@EnableJpaAuditingを作成する
@WebMvcTestは通常の@Configurationをスキャンしません.
すべてのテストに成功しました.次のtestImplementationセクションでは、上記の問題を解決しましたが、テストエラー、buildが発生しました.gradleに追加されたコード.
スプリングの安全性テストが可能になりました.
既存
直ちにAPIを呼び出すことができ、テストコードを構成しても直ちにAPIを呼び出すことができる.
セキュリティオプションの有効化
認証されたユーザーのみがAPIを呼び出すことができます
をクリックして全体テストを行います.
ニンジンテストに失敗
失敗の原因
CustomAuthe 2 UserServiceの作成に必要なソーシャルログイン設定はありません.
=>src/mainは設定されていますが、src/testでは設定されていません.
元のテスト時のアプリケーション.propertiesは自動的にインポートされます.(テスト中ではなくmain中の場合)
ただし、他のアプリケーション-oauth.properties(ソーシャルログインに関連する設定値ファイル)などのファイルをインポートしないため、エラーが発生しました.
だから私はあなたに作ってあげます.テストなので実際のGoogle連動は行わないので仮定定値を登録します.
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.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
これは、スプリングセキュリティ設定が許可されていないユーザーの要求を移動するためです.
認証ユーザをランダムに追加し、APIのみをテストします.
build.gradeにspring-security-testを追加します.
次に、PostsApiControllerTestの2つのテスト方法にランダムユーザー認証を追加します.
仮想ロールは、認証された偽のユーザーを作成して使用し、ロールに権限を追加できます.
これは、ROLE USER権限を持つユーザがAPIを要求するのと同じ効果である.
これでもすべてのテストに合格できません.
@WithMockUserはMockMvcでのみ動作するためです.
現在PostsApiControllerTestは@SpringBootTestのみで、MockMvcは全く使用されていません.
@SpringBootTestでMockMvcを使用します。
package com.chanmi.book.springboot.web;
import com.chanmi.book.springboot.domain.posts.Posts;
import com.chanmi.book.springboot.domain.posts.PostsRepository;
import com.chanmi.book.springboot.web.dto.PostsSaveRequestDto;
import com.chanmi.book.springboot.web.dto.PostsUpdateRequestDto;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.boot.test.web.client.TestRestTemplate;
import org.springframework.boot.web.server.LocalServerPort;
import org.springframework.http.*;
import org.springframework.security.test.context.support.WithMockUser;
import org.springframework.test.context.junit4.SpringRunner;
import org.springframework.test.web.servlet.MockMvc;
import org.springframework.test.web.servlet.MockMvcBuilder;
import org.springframework.test.web.servlet.setup.MockMvcBuilders;
import org.springframework.web.context.WebApplicationContext;
import java.util.List;
import static org.assertj.core.api.Assertions.assertThat;
//@SpringBootTest에서 MockMvc를 사용하기 위한 추가
import static org.springframework.security.test.web.servlet.setup.SecurityMockMvcConfigurers.springSecurity;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.*;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
@RunWith(SpringRunner.class)
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
public class PostsApiControllerTest {
@LocalServerPort
private int port;
//@WebMvcTest 사용하지 않고
@Autowired
private TestRestTemplate restTemplate;
@Autowired
private PostsRepository postsRepository;
//@SpringBootTest에서 MockMvc를 사용하기 위한 =========
@Autowired
private WebApplicationContext context;
private MockMvc mvc;
//매번 테스트 시작 전에 MockMvc 인스턴스 생성
@Before
public void setup(){
mvc = MockMvcBuilders
.webAppContextSetup(context)
.apply(springSecurity())
.build();
}
@After
public void tearDown() throws Exception{
postsRepository.deleteAll();
}
@Test
@WithMockUser(roles = "USER")//임의 사용자 인증 추가
public void save_Posts() throws Exception{
//given
String title = "title";
String content = "content";
//데이터를 넣어서 DTO 하나 생성
PostsSaveRequestDto requestDto = PostsSaveRequestDto.builder()
.title(title)
.content(content)
.author("author")
.build();
String url = "http://localhost:" + port + "api/v1/posts";
//when, 생성한 Dto가지고 url로 post/Long으로 반환받음
//ResponseEntity<Long> responseEntity = restTemplate.postForEntity(url, requestDto, Long.class); //MockMvc 사용 추가 후 주석 처리
//MockMvc 사용 추가
//생성된 MockMvc를 통해 API 테스트, 본문(Body) 영역을 ObjectMapper를 통해 문자열 JSON으로 변환
mvc.perform(post(url)
.contentType(MediaType.APPLICATION_JSON_UTF8)
.content(new ObjectMapper().writeValueAsString(requestDto)))
.andExpect(status().isOk());
//then
//MockMvc 사용 추가 후 주석 처리
// assertThat(responseEntity.getStatusCode()).isEqualTo(HttpStatus.OK);
// assertThat(responseEntity.getBody()).isGreaterThan(0L);//바디 > 0L
//데이터 잘 등록됐는지 확인
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 update_posts() throws Exception{
//근데 내가 빡대갈이라 그러는데, 여기서 Posts 클래스로 만들었네..?왜..?
//given
Posts savedPosts = postsRepository.save(Posts.builder()
.title("title")
.content("content")
.author("author")
.build());
Long updateId = savedPosts.getId();
String expectedTitle = "title2";
String expectedContent = "content2";
PostsUpdateRequestDto requestDto = PostsUpdateRequestDto.builder()
.title(expectedTitle)
.content(expectedContent)
.build();
String url = "http://localhost:" + port + "/api/v1/posts/" + updateId;
HttpEntity<PostsUpdateRequestDto> requestEntity = new HttpEntity<>(requestDto);
//when
// ResponseEntity<Long> responseEntity = restTemplate.exchange(url, HttpMethod.PUT, requestEntity, Long.class); //MockMvc 사용 추가 후 주석 처리
//MockMvc 사용 추가
//생성된 MockMvc를 통해 API 테스트, 본문(Body) 영역을 ObjectMapper를 통해 문자열 JSON으로 변환
mvc.perform(put(url)
.contentType(MediaType.APPLICATION_JSON_UTF8)
.content(new ObjectMapper().writeValueAsString(requestDto)))
.andExpect(status().isOk());
//then
//MockMvc 사용 추가 후 주석 처리
// assertThat(responseEntity.getStatusCode()).isEqualTo(HttpStatus.OK);
// assertThat(responseEntity.getBody()).isGreaterThan(0L);
List<Posts> all = postsRepository.findAll();
assertThat(all.get(0).getTitle()).isEqualTo(expectedTitle);
assertThat(all.get(0).getContent()).isEqualTo(expectedContent);
}
}
追加された部分とコメント処理された部分にはコメントが付けられています.これによりposts部分を迂回するだけでテストは成功した.1番スプリングの安全設定は正常に動作していますが、@WebMvcTestはCustomOuth 2 UserServiceをスキャンしないため、問題が発生しています
@WebMvcTest@ControllerAddiceと@Controllerを読み込みます.WebSecurityコンフィギュレータアダプタとWebMvcConfigureが含まれます.つまり、@Repository、@Service、@Componentはスキャンターゲットではないため、セキュリティ構成が読み込まれました.ただし、セキュリティ構成の作成に必要なCustomOuth 2 UserServiceを読み込めないため、エラーが発生しました
質疑応答:スキャンターゲットからSecurityConfigを削除
変更
@WithMockUserが偽認証ユーザーを生成
でもそれは間違いだ
このエラーは、アプリケーションクラスの@EnableJpaAuditingによって発生します.
@EnableJpaAuditingには少なくとも1つの@Entityクラスが必要です.でも@WebMcTestなのでもちろんありません
@EnableJpaAuditingは@SpringBootApplicationと一緒なので@WebMcTestでもスキャンします.
だから@EnableJpaAuditingと@SpringBootApplicationを分けます.
Application.Javaから構文を削除する
コンフィグパッケージにJpaConfig->@EnableJpaAuditingを作成する
@WebMvcTestは通常の@Configurationをスキャンしません.
スプリングの安全性テストが可能になりました.
Reference
この問題について(既存のテストにセキュリティを適用), 我々は、より多くの情報をここで見つけました https://velog.io/@channi/기존-테스트에-시큐리티-적용テキストは自由に共有またはコピーできます。ただし、このドキュメントのURLは参考URLとして残しておいてください。
Collection and Share based on the CC Protocol