web-dev-qa-db-ja.com

Spring Security HttpSecurity構成テスト

私はいくつかの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.RELEASEspring-security-core-4.2.2-releaseを使用します。

Clarification1:/api/**エンドポイントの1つを介して推移的にテストするのではなく、SecurityConfigurationをできるだけ直接テストしたい独自の@PreAuthorizeセキュリティがある場合があります。

Clarification2:これに似たものを希望します WebSecurityConfigurerAdapterTests

Clarification3:Spring Securityレイヤーで@Autowireをテストしたいのですが、理想的にはHttpSecurityです。

18
JJ Zabkar

したがって、誰かが.antMatchers("/api/**").antMatchers("/WRONG_PATH/**")に変更した場合、それを理解するテストがあることを確認したいですか?

HttpSecurityを使用して定義するルールは、1つまたは複数のFilterChainProxySecurityFilterChainを構成し、それぞれにフィルターのリストを含めます。 UsernamePasswordAuthenticationFilter(フォームベースのログインに使用)などの各フィルターには、スーパークラスRequestMatcherAbstractAuthenticationProcessingFilterが定義されています。問題は、RequestMatcher現在に12の異なる実装があるインターフェースであり、これにはAndRequestMatcherOrRequestMatcherが含まれるため、マッチングロジックが常に単純であるとは限らないということです。そして最も重要なことに、RequestMatcherにはboolean matches(HttpServletRequest request)のメソッドが1つだけあり、実装は構成を公開しないことが多いため、リフレクションを使用して各RequestMatcher実装のプライベート構成にアクセスする必要があります(将来的に変更される可能性があります)。 。

このパスをたどり、テストにFilterChainProxyを自動配線し、リフレクションを使用して構成をリバースエンジニアリングする場合は、実装の依存関係をすべて考慮する必要があります。たとえば、WebSecurityConfigurerAdapterにはデフォルトのフィルターリストがあり、リリースごとに変更される可能性があります。無効にしない限り、フィルターが無効になっている場合は、すべてのフィルターを明示的に定義する必要があります。さらに、新しいフィルターとRequestMatchersが時間の経過とともに追加される可能性があります。または、あるバージョンのSpring SecurityでHttpSecurityによって生成されたフィルターチェーンが次のバージョンで少し異なる場合があります(可能性は低いですが、それでも可能です)。

Spring Security構成の一般的なテストを書くことは技術的には可能ですが、簡単に行うことはできず、Spring Securityフィルターはこれをサポートするように設計されていません。私は2010年以来、Spring Securityと幅広く協力してきましたが、そのようなテストを行う必要はありませんでした。個人的には、それを実装するのは時間の無駄だと思います。セキュリティレイヤーとビジネスロジックを暗黙的にテストする統合テストを簡単に作成できるテストフレームワークの作成に費やす時間は、はるかによくなると思います。

4
Klaus Groenbaek

モック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を抽出する必要があります。

これがあなたが必要としていたことを願っています。

2
Klaus Groenbaek

存在するエンドポイントをプログラムで知りたい場合は、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を受け入れる必要があります。

新しいエンドポイントを導入するときに開発者がセキュリティ関連のミスをするのではないかと心配している場合は、エンドポイントのロジックについても同様に心配することになります。あなたのセキュリティも。

1
Klaus Groenbaek

以下のテストケースは、あなたが望むものを達成するのに役立ちます。これは、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のセキュリティコンテキストをテストできるようになります。

1
Ashwin Gupta

ボックスの外側を少し考えて、別の方法で質問に答えると、静的な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が正確であることがわかっていると仮定すると、これが必要な場合、このアプローチは単体テスト可能なバックストップを提供します。

0
theINtoy