私はいくつかのantMatchers
パスを持つSpring Boot + Spring Securityアプリケーションを持っています。一部のfullyAuthenticated()
、一部のpermitAll()
。
SecurityConfiguration
が/api/**
(および最終的にはその他の)の下のエンドポイントを正しく保護していることを確認するテストを作成するにはどうすればよいですか?
public class SecurityConfiguration extends WebSecurityConfigurerAdapter {
protected void configure(HttpSecurity http) throws Exception {
http
//...
.antMatchers("/api/**").fullyAuthenticated()
}
}
spring-boot-1.5.2.RELEASE
、spring-security-core-4.2.2-release
を使用します。
Clarification1:/api/**
エンドポイントの1つを介して推移的にテストするのではなく、SecurityConfiguration
をできるだけ直接テストしたい独自の@PreAuthorize
セキュリティがある場合があります。
Clarification2:これに似たものを希望します WebSecurityConfigurerAdapterTests 。
Clarification3:Spring Securityレイヤーで@Autowire
をテストしたいのですが、理想的にはHttpSecurity
です。
したがって、誰かが.antMatchers("/api/**")
を.antMatchers("/WRONG_PATH/**")
に変更した場合、それを理解するテストがあることを確認したいですか?
HttpSecurity
を使用して定義するルールは、1つまたは複数のFilterChainProxy
でSecurityFilterChain
を構成し、それぞれにフィルターのリストを含めます。 UsernamePasswordAuthenticationFilter
(フォームベースのログインに使用)などの各フィルターには、スーパークラスRequestMatcher
でAbstractAuthenticationProcessingFilter
が定義されています。問題は、RequestMatcher
が現在に12の異なる実装があるインターフェースであり、これにはAndRequestMatcher
とOrRequestMatcher
が含まれるため、マッチングロジックが常に単純であるとは限らないということです。そして最も重要なことに、RequestMatcher
にはboolean matches(HttpServletRequest request)
のメソッドが1つだけあり、実装は構成を公開しないことが多いため、リフレクションを使用して各RequestMatcher
実装のプライベート構成にアクセスする必要があります(将来的に変更される可能性があります)。 。
このパスをたどり、テストにFilterChainProxy
を自動配線し、リフレクションを使用して構成をリバースエンジニアリングする場合は、実装の依存関係をすべて考慮する必要があります。たとえば、WebSecurityConfigurerAdapter
にはデフォルトのフィルターリストがあり、リリースごとに変更される可能性があります。無効にしない限り、フィルターが無効になっている場合は、すべてのフィルターを明示的に定義する必要があります。さらに、新しいフィルターとRequestMatchers
が時間の経過とともに追加される可能性があります。または、あるバージョンのSpring SecurityでHttpSecurity
によって生成されたフィルターチェーンが次のバージョンで少し異なる場合があります(可能性は低いですが、それでも可能です)。
Spring Security構成の一般的なテストを書くことは技術的には可能ですが、簡単に行うことはできず、Spring Securityフィルターはこれをサポートするように設計されていません。私は2010年以来、Spring Securityと幅広く協力してきましたが、そのようなテストを行う必要はありませんでした。個人的には、それを実装するのは時間の無駄だと思います。セキュリティレイヤーとビジネスロジックを暗黙的にテストする統合テストを簡単に作成できるテストフレームワークの作成に費やす時間は、はるかによくなると思います。
モックMVCは、モックを作成するのはHttpレイヤーだけなので、セキュリティ構成を確認するには十分です。ただし、Spring Bootアプリケーション、Tomcatサーバーなどすべてを本当にテストする場合は、次のように_@SpringBootTest
_を使用する必要があります。
_@RunWith(SpringJUnit4ClassRunner.class)
@SpringBootTest(webEnvironment= SpringBootTest.WebEnvironment.RANDOM_PORT)
public class NoGoServiceTest {
@LocalServerPort
private int port;
private <T> T makeDepthRequest(NoGoRequest request, NoGoResponse response, String path, Class<T> responseClass) {
testService.addRequestResponseMapping(request, response);
RestTemplate template = new RestTemplate();
HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.APPLICATION_JSON);
headers.setAccept(Lists.newArrayList(MediaType.APPLICATION_JSON));
headers.add("Authorization", "Bearer " + tokenProvider.getToken());
RequestEntity<NoGoRequest> requestEntity = new RequestEntity<>(request, headers, HttpMethod.POST, getURI(path));
ResponseEntity<T> responseEntity = template.exchange(requestEntity, responseClass);
return responseEntity.getBody();
}
@SneakyThrows(URISyntaxException.class)
private URI getURI(String path) {
return new URI("http://localhost:" +port + "/nogo" + path);
}
// Test that makes request using `makeDepthRequest`
}
_
このコードは、オープンソースプロジェクトから取得したテストの一部です( https://github.com/maritime-web/NoGoService )。基本的な考え方は、ランダムなポートでテストを開始し、Springがテストのフィールドに注入することです。これにより、コントローラーと同じDTOクラスを使用して、URLを構築し、SpringsのRestTemplate
を使用してサーバーにhttpリクエストを送信できます。認証メカニズムがBasicまたはTokenの場合、この例のように、正しいAuthorizationヘッダーを追加するだけです。フォーム認証を使用する場合、最初に_GET /login
_、次にCSRFトークンとJSessionId Cookie、およびPOST
を_/login
_への資格情報とともに抽出する必要があるため、少し難しくなります。ログイン後は、セキュリティ上の理由からログイン後にsessionIdが変更されるため、新しいJSessionId Cookieを抽出する必要があります。
これがあなたが必要としていたことを願っています。
存在するエンドポイントをプログラムで知りたい場合は、RequestHandlerProvider
のリストをテストに自動配線し、公開されているパスに基づいてそれらをフィルター処理できます。
@Autowired
List<RequestHandlerProvider> handlerProviders;
@Test
public void doTest() {
for (RequestHandlerProvider handlerProvider : handlerProviders) {
for (RequestHandler requestHandler : handlerProvider.requestHandlers()) {
for (String pattern : requestHandler.getPatternsCondition().getPatterns()) {
// call the endpoint without security and check that you get 401
}
}
}
}
RequestHandlerProvider
を使用することで、SpringFoxは、APIのSwagger定義を構築するときに、使用可能なエンドポイントとそのシグネチャを決定します。
各エンドポイントの正しい入力の作成に長い時間を費やさない限り、有効なセキュリティトークンを含めると、エンドポイントから200 OKが返されないため、正しい応答として400を受け入れる必要があります。
新しいエンドポイントを導入するときに開発者がセキュリティ関連のミスをするのではないかと心配している場合は、エンドポイントのロジックについても同様に心配することになります。あなたのセキュリティも。
以下のテストケースは、あなたが望むものを達成するのに役立ちます。これは、Webセキュリティ構成をテストする統合テストであり、TDD駆動のすべてのコードに対して同様のテストが行われています。
@RunWith(SpringRunner.class)
@SpringBootTest(classes = Application.class)
@WebAppConfiguration
public class WebConfigIT {
private MockMvc mockMvc;
@Autowired
private WebApplicationContext webApplicationContext;
@Autowired
private FilterChainProxy springSecurityFilterChain;
@Before
public void setup() throws Exception {
mockMvc = webAppContextSetup(webApplicationContext)
.addFilter(springSecurityFilterChain)
.build();
}
@Test
public void testAuthenticationAtAPIURI() throws Exception {
mockMvc.perform(get("/api/xyz"))
.andExpect(status.is3xxRedirection());
}
これはエンドポイントの明示的なテストを実行しているように見えますが(TDDを実行する場合に実行する必要があるテストです)、これにより、Spring Security Filter Chainがコンテキストに組み込まれ、APPのセキュリティコンテキストをテストできるようになります。
ボックスの外側を少し考えて、別の方法で質問に答えると、静的なString []を定義するだけの方が簡単ではありません。
public class SecurityConfiguration extends WebSecurityConfigurerAdapter {
public static final String[] FULLY_AUTH_PUBLIC_URLS = {"/api/**", "/swagger-resources/**", "/health", "/info" };
protected void configure(HttpSecurity http) throws Exception {
http
//...
.antMatchers(FULLY_AUTH_PUBLIC_URLS).fullyAuthenticated()
}
}
...
そして、テストの目的がパブリックURLに変更が加えられていないことを確認することである場合、既知のリストをテストするだけですか?
ここでの前提は、Spring Securityが機能し、テストされていることです。そのため、私たちがテストしているのは、パブリックURLのリストが変更されていないことだけです。彼らが変更した場合、テストは失敗し、これらの値を変更するドラゴンがいることを開発者に強調する必要がありますか?私はこれが明確化をカバーしないことを理解していますが、提供された静的パブリックURLが正確であることがわかっていると仮定すると、これが必要な場合、このアプローチは単体テスト可能なバックストップを提供します。