18. テスト

このセクションでは、Spring Security が提供するテストサポートについて説明します。

[Tip] ヒント

Spring Security テストサポートを使用するには、spring-security-test-5.2.8.BUILD-SNAPSHOT.jar をプロジェクトの依存関係として含める必要があります。

18.1 テスト方法のセキュリティ

このセクションでは、Spring Security のテストサポートを使用して、メソッドベースのセキュリティをテストする方法を示します。まず、MessageService を導入します。MessageService では、アクセスするためにユーザーの認証が必要です。

public class HelloMessageService implements MessageService {

    @PreAuthorize("authenticated")
    public String getMessage() {
        Authentication authentication = SecurityContextHolder.getContext()
            .getAuthentication();
        return "Hello " + authentication;
    }
}

getMessage の結果は、現在の Spring Security Authentication に対して「Hello」を示すストリングです。出力の例を以下に表示します。

Hello org.springframew[email protected] (英語)  ca25360: Principal: [email protected] (英語)  : Username: user; Password: [PROTECTED]; Enabled: true; AccountNonExpired: true; credentialsNonExpired: true; AccountNonLocked: true; Granted Authorities: ROLE_USER; Credentials: [PROTECTED]; Authenticated: true; Details: null; Granted Authorities: ROLE_USER

18.1.1 セキュリティテストのセットアップ

Spring Security テストサポートを使用する前に、セットアップを実行する必要があります。以下に例を示します。

@RunWith(SpringJUnit4ClassRunner.class) 1
@ContextConfiguration 2
public class WithMockUserTests {

これは、Spring Security テストのセットアップ方法の基本的な例です。ハイライトは次のとおりです。

1

@RunWith は、ApplicationContext を作成するように spring-test モジュールに指示します。これは、既存の Spring Test サポートを使用するのと同じです。詳細については、Spring リファレンス (英語) を参照してください。

2

@ContextConfiguration は、ApplicationContext の作成に使用する構成を spring-test に指示します。構成が指定されていないため、デフォルトの構成場所が試されます。これは、既存の Spring Test サポートを使用するのと同じです。詳細については、Spring リファレンス (英語) を参照してください。

[Note] メモ

Spring Security は、WithSecurityContextTestExecutionListener を使用して Spring Test サポートにフックします。これにより、テストが正しいユーザーで実行されることが保証されます。これは、テストを実行する前に SecurityContextHolder にデータを入力することによって行われます。リアクティブメソッドセキュリティを使用している場合は、ReactiveSecurityContextHolder にデータを入力する ReactorContextTestExecutionListener も必要になります。テストが完了すると、SecurityContextHolder がクリアされます。Spring Security 関連のサポートのみが必要な場合は、@ContextConfiguration を @SecurityTestExecutionListeners に置き換えることができます。

@PreAuthorize アノテーションを HelloMessageService に追加したことを思い出してください。それを呼び出すには認証されたユーザーが必要です。次のテストを実行した場合、次のテストに合格すると予想されます。

@Test(expected = AuthenticationCredentialsNotFoundException.class)
public void getMessageUnauthenticated() {
    messageService.getMessage();
}

18.1.2 @WithMockUser

問題は、「特定のユーザーとしてどのようにテストを最も簡単に実行できるか」です。答えは @WithMockUser を使用することです。次のテストは、ユーザー名「user」、パスワード「password」、およびロール「ROLE_USER」を持つユーザーとして実行されます。

@Test
@WithMockUser
public void getMessageWithMockUser() {
String message = messageService.getMessage();
...
}

具体的には、次のことが当てはまります。

  • ユーザーをモックしているため、ユーザー名が「user」のユーザーは存在する必要はありません。
  • SecurityContext にある Authentication は、タイプ UsernamePasswordAuthenticationToken です
  • Authentication のプリンシパルは Spring Security の User オブジェクトです
  • User のユーザー名は「user」、パスワードは「password」で、「ROLE_USER」という名前の単一の GrantedAuthority が使用されます。

多くのデフォルトを活用できるため、この例は素晴らしいです。別のユーザー名でテストを実行したい場合はどうなるでしょうか? 次のテストは、ユーザー名「customUser」で実行されます。繰り返しますが、ユーザーは実際に存在する必要はありません。

@Test
@WithMockUser("customUsername")
public void getMessageWithMockUserCustomUsername() {
    String message = messageService.getMessage();
...
}

ロールを簡単にカスタマイズすることもできます。例: このテストは、ユーザー名「admin」とロール「ROLE_USER」および「ROLE_ADMIN」で呼び出されます。

@Test
@WithMockUser(username="admin",roles={"USER","ADMIN"})
public void getMessageWithMockUserCustomUser() {
    String message = messageService.getMessage();
    ...
}

値に自動的に ROLE_ のプレフィックスを付けたくない場合は、authorities 属性を活用できます。例: このテストは、ユーザー名「admin」と権限「USER」および「ADMIN」で呼び出されます。

@Test
@WithMockUser(username = "admin", authorities = { "ADMIN", "USER" })
public void getMessageWithMockUserCustomAuthorities() {
    String message = messageService.getMessage();
    ...
}

もちろん、すべてのテストメソッドにアノテーションを付けるのは少し面倒です。代わりに、クラスレベルでアノテーションを配置することができ、すべてのテストは指定されたユーザーを使用します。例: 以下は、ユーザー名が「admin」、パスワードが「password」、ロールが「ROLE_USER」および「ROLE_ADMIN」のユーザーですべてのテストを実行します。

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration
@WithMockUser(username="admin",roles={"USER","ADMIN"})
public class WithMockUserTests {

デフォルトでは、SecurityContext は TestExecutionListener.beforeTestMethod イベント中に設定されます。これは、JUnit の @Before の前に発生することと同等です。これは、JUnit の @Before の後、テストメソッドが呼び出される前の TestExecutionListener.beforeTestExecution イベント中に発生するように変更できます。

@WithMockUser(setupBefore = TestExecutionEvent.TEST_EXECUTION)

18.1.3 @WithAnonymousUser

@WithAnonymousUser を使用すると、匿名ユーザーとして実行できます。これは、特定のユーザーでほとんどのテストを実行したいが、匿名ユーザーとしていくつかのテストを実行したい場合に特に便利です。例: 以下は、@WithMockUser と anonymous ユーザーとして anonymous を使用して withMockUser1 と withMockUser2 を実行します。

@RunWith(SpringJUnit4ClassRunner.class)
@WithMockUser
public class WithUserClassLevelAuthenticationTests {

    @Test
    public void withMockUser1() {
    }

    @Test
    public void withMockUser2() {
    }

    @Test
    @WithAnonymousUser
    public void anonymous() throws Exception {
        // override default to run as anonymous user
    }
}

デフォルトでは、SecurityContext は TestExecutionListener.beforeTestMethod イベント中に設定されます。これは、JUnit の @Before の前に発生することと同等です。これは、JUnit の @Before の後、テストメソッドが呼び出される前の TestExecutionListener.beforeTestExecution イベント中に発生するように変更できます。

@WithAnonymousUser(setupBefore = TestExecutionEvent.TEST_EXECUTION)

18.1.4 @WithUserDetails

@WithMockUser は開始するのに非常に便利な方法ですが、すべてのインスタンスで機能するとは限りません。例: Authentication プリンシパルが特定のタイプであることをアプリケーションが予期することは一般的です。これは、アプリケーションがプリンシパルをカスタムタイプとして参照し、Spring Security での結合を削減できるようにするために行われます。

多くの場合、カスタムプリンシパルは、UserDetails とカスタム型の両方を実装するオブジェクトを返すカスタム UserDetailsService によって返されます。このような状況では、カスタム UserDetailsService を使用してテストユーザーを作成すると便利です。それがまさに @WithUserDetails が行うことです。

UserDetailsService が Bean として公開されていると仮定すると、型 UsernamePasswordAuthenticationToken の Authentication と、ユーザー名「user」で UserDetailsService から返されるプリンシパルを使用して、次のテストが呼び出されます。

@Test
@WithUserDetails
public void getMessageWithUserDetails() {
    String message = messageService.getMessage();
    ...
}

UserDetailsService からユーザーを検索するために使用されるユーザー名をカスタマイズすることもできます。例: このテストは、"customUsername" というユーザー名で UserDetailsService から返されるプリンシパルで実行されます。

@Test
@WithUserDetails("customUsername")
public void getMessageWithUserDetailsCustomUsername() {
    String message = messageService.getMessage();
    ...
}

UserDetailsService を検索するために、明示的な Bean 名を提供することもできます。例: このテストでは、Bean 名「myUserDetailsService」の UserDetailsService を使用して、「customUsername」のユーザー名を検索します。

@Test
@WithUserDetails(value="customUsername", userDetailsServiceBeanName="myUserDetailsService")
public void getMessageWithUserDetailsServiceBeanName() {
    String message = messageService.getMessage();
    ...
}

@WithMockUser のように、すべてのテストが同じユーザーを使用するように、アノテーションをクラスレベルに配置することもできます。ただし、@WithMockUser とは異なり、@WithUserDetails ではユーザーが存在する必要があります。

デフォルトでは、SecurityContext は TestExecutionListener.beforeTestMethod イベント中に設定されます。これは、JUnit の @Before の前に発生することと同等です。これは、JUnit の @Before の後、テストメソッドが呼び出される前の TestExecutionListener.beforeTestExecution イベント中に発生するように変更できます。

@WithUserDetails(setupBefore = TestExecutionEvent.TEST_EXECUTION)

18.1.5 @WithSecurityContext

カスタム Authentication プリンシパルを使用していない場合は、@WithMockUser が優れた選択肢であることがわかりました。次に、@WithUserDetails を使用すると、カスタム UserDetailsService を使用して Authentication プリンシパルを作成できるが、ユーザーが存在する必要があることがわかりました。ここで、最も柔軟性が高いオプションが表示されます。

@WithSecurityContext を使用して必要な SecurityContext を作成する独自のアノテーションを作成できます。例: 以下に示すように、@WithMockCustomUser という名前のアノテーションを作成できます。

@Retention(RetentionPolicy.RUNTIME)
@WithSecurityContext(factory = WithMockCustomUserSecurityContextFactory.class)
public @interface WithMockCustomUser {

    String username() default "rob";

    String name() default "Rob Winch";
}

@WithMockCustomUser に @WithSecurityContext アノテーションが付けられていることがわかります。これは、Spring Security テストサポートに、テスト用に SecurityContext を作成する予定であることを示すものです。@WithSecurityContext アノテーションでは、@WithMockCustomUser アノテーションを指定して、新しい SecurityContext を作成する SecurityContextFactory を指定する必要があります。WithMockCustomUserSecurityContextFactory の実装は次のとおりです。

public class WithMockCustomUserSecurityContextFactory
    implements WithSecurityContextFactory<WithMockCustomUser> {
    @Override
    public SecurityContext createSecurityContext(WithMockCustomUser customUser) {
        SecurityContext context = SecurityContextHolder.createEmptyContext();

        CustomUserDetails principal =
            new CustomUserDetails(customUser.name(), customUser.username());
        Authentication auth =
            new UsernamePasswordAuthenticationToken(principal, "password", principal.getAuthorities());
        context.setAuthentication(auth);
        return context;
    }
}

これで、テストクラスまたはテストメソッドに新しいアノテーションを付けることができ、Spring Security の WithSecurityContextTestExecutionListener により、SecurityContext が適切に読み込まれます。

独自の WithSecurityContextFactory 実装を作成する場合、標準の Spring アノテーションでアノテーションを付けることができることを知っておくと便利です。例: WithUserDetailsSecurityContextFactory は @Autowired アノテーションを使用して UserDetailsService を取得します。

final class WithUserDetailsSecurityContextFactory
    implements WithSecurityContextFactory<WithUserDetails> {

    private UserDetailsService userDetailsService;

    @Autowired
    public WithUserDetailsSecurityContextFactory(UserDetailsService userDetailsService) {
        this.userDetailsService = userDetailsService;
    }

    public SecurityContext createSecurityContext(WithUserDetails withUser) {
        String username = withUser.value();
        Assert.hasLength(username, "value() must be non-empty String");
        UserDetails principal = userDetailsService.loadUserByUsername(username);
        Authentication authentication = new UsernamePasswordAuthenticationToken(principal, principal.getPassword(), principal.getAuthorities());
        SecurityContext context = SecurityContextHolder.createEmptyContext();
        context.setAuthentication(authentication);
        return context;
    }
}

デフォルトでは、SecurityContext は TestExecutionListener.beforeTestMethod イベント中に設定されます。これは、JUnit の @Before の前に発生することと同等です。これは、JUnit の @Before の後、テストメソッドが呼び出される前の TestExecutionListener.beforeTestExecution イベント中に発生するように変更できます。

@WithSecurityContext(setupBefore = TestExecutionEvent.TEST_EXECUTION)

18.1.6 メタアノテーションのテスト

テスト内で同じユーザーを頻繁に再利用する場合、属性を繰り返し指定する必要はありません。例: ユーザー名が「admin」で、ロール ROLE_USER および ROLE_ADMIN の管理ユーザーに関連する多くのテストがある場合、次のように記述する必要があります。

@WithMockUser(username="admin",roles={"USER","ADMIN"})

これをどこでも繰り返すのではなく、メタアノテーションを使用できます。例: WithMockAdmin という名前のメタアノテーションを作成できます。

@Retention(RetentionPolicy.RUNTIME)
@WithMockUser(value="rob",roles="ADMIN")
public @interface WithMockAdmin { }

これで、より詳細な @WithMockUser と同じ方法で @WithMockAdmin を使用できます。

メタアノテーションは、上記のテストアノテーションのいずれかと連携します。例: これは、@WithUserDetails("admin") のメタアノテーションも作成できることを意味します。

18.2 Spring MVC テスト統合

Spring Security は Spring MVC テストとの包括的な統合を提供する

18.2.1 MockMvc および Spring Security のセットアップ

Spring MVC テストで Spring Security を使用するには、Spring Security FilterChainProxy を Filter として追加する必要があります。Spring MVC テストでアノテーション付きでユーザーとして実行するをサポートするには、Spring Security の TestSecurityContextHolderPostProcessor を追加する必要もあります。これは、Spring Security の SecurityMockMvcConfigurers.springSecurity() を使用して実行できます。例:

[Note] メモ

Spring Security のテストサポートには、spring-test-4.1.3.RELEASE 以降が必要です。

import static org.springframework.security.test.web.servlet.setup.SecurityMockMvcConfigurers.*;

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration
@WebAppConfiguration
public class CsrfShowcaseTests {

    @Autowired
    private WebApplicationContext context;

    private MockMvc mvc;

    @Before
    public void setup() {
        mvc = MockMvcBuilders
                .webAppContextSetup(context)
                .apply(springSecurity()) 1
                .build();
    }

...

1

SecurityMockMvcConfigurers.springSecurity() は、Spring Security と Spring MVC テストを統合するために必要なすべての初期セットアップを実行します。

18.2.2 SecurityMockMvcRequestPostProcessors

Spring MVC テストは、リクエストを変更するために使用できる RequestPostProcessor と呼ばれる便利なインターフェースを提供します。Spring Security は、テストを容易にする多数の RequestPostProcessor 実装を提供します。Spring Security の RequestPostProcessor 実装を使用するには、次の静的インポートが使用されていることを確認してください。

import static org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestPostProcessors.*;

CSRF 保護を使用したテスト

安全でない HTTP メソッドをテストし、Spring Security の CSRF 保護を使用する場合、有効な CSRF トークンをリクエストに含める必要があります。次を使用して、リクエストパラメーターとして有効な CSRF トークンを指定するには:

mvc
    .perform(post("/").with(csrf()))

必要に応じて、代わりにヘッダーに CSRF トークンを含めることができます。

mvc
    .perform(post("/").with(csrf().asHeader()))

次を使用して、無効な CSRF トークンの提供をテストすることもできます。

mvc
    .perform(post("/").with(csrf().useInvalidToken()))

Spring MVC テストでユーザーとしてテストを実行する

特定のユーザーとしてテストを実行することが望ましい場合がよくあります。ユーザーにデータを入力するには、2 つの簡単な方法があります。

Spring MVC テストで RequestPostProcessor を使用してユーザーとして実行する

ユーザーを現在の HttpServletRequest に関連付けるために利用できるオプションがいくつかあります。例: 以下は、ユーザー名「user」、パスワード「password」、およびロール「ROLE_USER」を持つユーザー(存在する必要はありません)として実行されます。

[Note] メモ

サポートは、ユーザーを HttpServletRequest に関連付けることで機能します。リクエストを SecurityContextHolder に関連付けるには、SecurityContextPersistenceFilter が MockMvc インスタンスに関連付けられていることを確認する必要があります。これを行ういくつかの方法は次のとおりです。

  • 適用(springSecurity())の呼び出し
  • Spring Security の FilterChainProxy を MockMvc に追加
  • SecurityContextPersistenceFilter を MockMvc インスタンスに手動で追加すると、MockMvcBuilders.standaloneSetup を使用する際に意味があります。
mvc
    .perform(get("/").with(user("user")))

簡単にカスタマイズできます。例: 以下は、ユーザー名「admin」、パスワード「pass」、ロール「ROLE_USER」および「ROLE_ADMIN」を持つユーザー(存在する必要はありません)として実行されます。

mvc
    .perform(get("/admin").with(user("admin").password("pass").roles("USER","ADMIN")))

使用したいカスタム UserDetails がある場合は、それも簡単に指定できます。例: 以下は、指定された UserDetails (存在する必要はありません)を使用して、指定された UserDetails のプリンシパルを持つ UsernamePasswordAuthenticationToken で実行します。

mvc
    .perform(get("/").with(user(userDetails)))

次を使用して匿名ユーザーとして実行できます。

mvc
    .perform(get("/").with(anonymous()))

これは、デフォルトのユーザーで実行していて、匿名ユーザーとしていくつかのリクエストを実行したい場合に特に便利です。

カスタム Authentication (存在する必要はありません)が必要な場合は、次を使用して行うことができます。

mvc
    .perform(get("/").with(authentication(authentication)))

以下を使用して SecurityContext をカスタマイズすることもできます。

mvc
    .perform(get("/").with(securityContext(securityContext)))

MockMvcBuilders のデフォルトリクエストを使用することで、すべてのリクエストに対して特定のユーザーとして実行することも保証できます。例: 以下は、ユーザー名「admin」、パスワード「password」、およびロール「ROLE_ADMIN」を持つユーザー(存在する必要はありません)として実行されます。

mvc = MockMvcBuilders
        .webAppContextSetup(context)
        .defaultRequest(get("/").with(user("user").roles("ADMIN")))
        .apply(springSecurity())
        .build();

多くのテストで同じユーザーを使用していることがわかった場合は、ユーザーをメソッドに移動することをお勧めします。例: CustomSecurityMockMvcRequestPostProcessors という名前の独自のクラスで以下を指定できます。

public static RequestPostProcessor rob() {
    return user("rob").roles("ADMIN");
}

これで、SecurityMockMvcRequestPostProcessors で静的インポートを実行し、テスト内でそれを使用できます。

import static sample.CustomSecurityMockMvcRequestPostProcessors.*;

...

mvc
    .perform(get("/").with(rob()))
Spring MVC テストでアノテーション付きでユーザーとして実行する

RequestPostProcessor を使用してユーザーを作成する代わりに、セクション 18.1: “ テスト方法のセキュリティ ” で説明されているアノテーションを使用できます。例: 以下は、ユーザー名が「user」、パスワードが「password」、ロールが「ROLE_USER」のユーザーでテストを実行します。

@Test
@WithMockUser
public void requestProtectedUrlWithUser() throws Exception {
mvc
        .perform(get("/"))
        ...
}

または、次のコマンドは、ユーザー名が「user」、パスワードが「password」、ロールが「ROLE_ADMIN」のユーザーでテストを実行します。

@Test
@WithMockUser(roles="ADMIN")
public void requestProtectedUrlWithUser() throws Exception {
mvc
        .perform(get("/"))
        ...
}

HTTP 基本認証のテスト

HTTP Basic で認証することは常に可能でしたが、ヘッダー名、形式、値のエンコードを覚えておくのは少し面倒でした。これは、Spring Security の httpBasicRequestPostProcessor を使用して実行できるようになりました。例: 以下のスニペット:

mvc
    .perform(get("/").with(httpBasic("user","password")))

HTTP リクエストに次のヘッダーが入力されていることを確認することにより、ユーザー名「user」とパスワード「password」でユーザーを認証するために HTTP Basic を使用しようとします。

Authorization: Basic dXNlcjpwYXNzd29yZA==

18.2.3 SecurityMockMvcRequestBuilders

Spring MVC テストは、テストで使用される MockHttpServletRequest を作成するために使用できる RequestBuilder インターフェースも提供します。Spring Security は、テストを容易にするために使用できる RequestBuilder 実装をいくつか提供します。Spring Security の RequestBuilder 実装を使用するには、次の静的インポートが使用されていることを確認してください。

import static org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestBuilders.*;

フォームベース認証のテスト

Spring Security のテストサポートを使用して、フォームベースの認証をテストするリクエストを簡単に作成できます。例: 以下は、ユーザー名「user」、パスワード「password」、および有効な CSRF トークンを使用して、「/login」に POST を送信します。

mvc
    .perform(formLogin())

リクエストは簡単にカスタマイズできます。例: 以下は、ユーザー名「admin」、パスワード「pass」、および有効な CSRF トークンを使用して、「/auth」に POST を送信します。

mvc
    .perform(formLogin("/auth").user("admin").password("pass"))

ユーザー名とパスワードが含まれるパラメーター名をカスタマイズすることもできます。例: これは、HTTP パラメーター「u」にユーザー名を、HTTP パラメーター「p」にパスワードを含めるように変更された上記のリクエストです。

mvc
    .perform(formLogin("/auth").user("u","admin").password("p","pass"))

ベアラー認証のテスト

リソースサーバーで承認されたリクエストを行うには、ベアラートークンが必要です。リソースサーバーが JWT 用に構成されている場合、これはベアラトークンに署名し、JWT 仕様に従ってエンコードする必要があることを意味します。これはすべて、特にテストの焦点ではない場合は非常に困難です。

幸いなことに、この困難を克服し、テストでベアラトークンの表現ではなく認証に焦点を当てることができるいくつかの簡単な方法があります。次に、そのうちの 2 つを見ていきます。

jwt() RequestPostProcessor

最初の方法は、RequestPostProcessor を使用することです。これらの最も単純なものは次のようになります。

mvc
    .perform(get("/endpoint").with(jwt()));

これにより、モック Jwt が作成され、認証 API を介して正しく渡され、認証メカニズムで検証できるようになります。

デフォルトでは、作成する JWT には次の特性があります。

{
  "headers" : { "alg" : "none" },
  "claims" : {
    "sub" : "user",
    "scope" : "read"
  }
}

そして、結果の Jwt は、テストされた場合、次のように合格します。

assertThat(jwt.getTokenValue()).isEqualTo("token");
assertThat(jwt.getHeaders().get("alg")).isEqualTo("none");
assertThat(jwt.getSubject()).isEqualTo("sub");
GrantedAuthority authority = jwt.getAuthorities().iterator().next();
assertThat(authority.getAuthority()).isEqualTo("read");

もちろん、これらの値は設定できます。

ヘッダーまたはクレームは、対応するメソッドで構成できます。

mvc
    .perform(get("/endpoint")
        .with(jwt().jwt(jwt -> jwt.header("kid", "one").claim("iss", "https://idp.example.org"))));
mvc
    .perform(get("/endpoint")
        .with(jwt().jwt(jwt -> jwt.claims(claims -> claims.remove("scope")))));

scope および scp クレームは、通常のベアラートークンリクエストの場合と同じようにここで処理されます。ただし、これは、テストに必要な GrantedAuthority インスタンスのリストを提供するだけでオーバーライドできます。

mvc
    .perform(get("/endpoint")
        .with(jwt().authorities(new SimpleGrantedAuthority("SCOPE_messages"))));

または、Jwt から Collection<GrantedAuthority> へのカスタムコンバーターがある場合は、それを使用して権限を取得することもできます。

mvc
    .perform(get("/endpoint")
        .with(jwt().authorities(new MyConverter())));

完全な Jwt を指定することもできます。Jwt.Builder (Javadoc)  は非常に便利です:

Jwt jwt = Jwt.withTokenValue("token")
    .header("alg", "none")
    .claim("sub", "user")
    .claim("scope", "read");

mvc
    .perform(get("/endpoint")
        .with(jwt().jwt(jwt)));
authentication()RequestPostProcessor

2 番目の方法は、authentication()RequestPostProcessor を使用することです。基本的に、次のように、独自の JwtAuthenticationToken をインスタンス化して、テストで提供できます。

Jwt jwt = Jwt.withTokenValue("token")
    .header("alg", "none")
    .claim("sub", "user")
    .build();
Collection<GrantedAuthority> authorities = AuthorityUtils.createAuthorityList("SCOPE_read");
JwtAuthenticationToken token = new JwtAuthenticationToken(jwt, authorities);

mvc
    .perform(get("/endpoint")
        .with(authentication(token)));

これらの代替として、JwtDecoder Bean 自体を @MockBean アノテーションでモックすることもできます。

ログアウトのテスト

標準の Spring MVC テストを使用するとかなり簡単ですが、Spring Security のテストサポートを使用して、テストを簡単にログアウトできます。例: 以下は、有効な CSRF トークンを使用して POST を「/logout」に送信します。

mvc
    .perform(logout())

投稿先の URL をカスタマイズすることもできます。例: 以下のスニペットは、有効な CSRF トークンとともに POST を「/signout」に送信します。

mvc
    .perform(logout("/signout"))

18.2.4 SecurityMockMvcResultMatchers

リクエストに関するセキュリティ関連のさまざまなアサーションを作成することが望ましい場合があります。このニーズに対応するため、Spring Security テストサポートは Spring MVC テストの ResultMatcher インターフェースを実装しています。Spring Security の ResultMatcher 実装を使用するには、次の静的インポートが使用されていることを確認してください。

import static org.springframework.security.test.web.servlet.response.SecurityMockMvcResultMatchers.*;

認証されていないアサーション

時には、MockMvc 呼び出しの結果に関連付けられた認証済みユーザーはいないと断言することが重要な場合があります。例: 無効なユーザー名とパスワードの送信をテストし、ユーザーが認証されていないことを確認できます。次のようなものを使用して、Spring Security のテストサポートでこれを簡単に行うことができます。

mvc
    .perform(formLogin().password("invalid"))
    .andExpect(unauthenticated());

認証されたアサーション

多くの場合、認証されたユーザーが存在すると断言する必要があります。例: 認証に成功したことを確認したい場合があります。次のコードスニペットを使用すると、フォームベースのログインが成功したことを確認できます。

mvc
    .perform(formLogin())
    .andExpect(authenticated());

ユーザーのロールをアサートしたい場合、以下に示すように以前のコードを改善できます。

mvc
    .perform(formLogin().user("admin"))
    .andExpect(authenticated().withRoles("USER","ADMIN"));

または、ユーザー名を確認することもできます。

mvc
    .perform(formLogin().user("admin"))
    .andExpect(authenticated().withUsername("admin"));

アサーションを組み合わせることもできます。

mvc
    .perform(formLogin().user("admin").roles("USER","ADMIN"))
    .andExpect(authenticated().withUsername("admin"));

認証に関して任意のアサーションを作成することもできます

mvc
    .perform(formLogin())
    .andExpect(authenticated().withAuthentication(auth ->
        assertThat(auth).isInstanceOf(UsernamePasswordAuthenticationToken.class)));
現行バージョンへ切り替える