Spring Boot入門:Controllerをユニットテストではアクセスに制限がない場合のControllerをユニットテストしました。
続いてこの記事では、Spring Securityを使っての認証および認可をしているURLに対するテストを行ってみます。
このテストをできるようになれば、認証/認可漏れのページも確認できるのでセキュリティ上の問題も減るはずです。
この記事のソースコード
この記事のソースコードはGithubに公開しています。Spring Boot入門:Spring Securityで認証と認可と同じコードです。
この記事を書く際にテストのコードを追加したので、場合によっては最新のものへ更新する必要があるかもしれません。ダウンロードしたフォルダに移動して、git pullしてください。
git pull
GithubからSpring BootプロジェクトをEclipseにインポートする方法は次の記事を参考にしてください。
Spring Securityで認証/認可をかけたURLのユニットテスト
今回はSpring Boot入門:Spring Securityで認証と認可のコードをテスト対象にします。テスト対象にするコードはそちらの記事で確認してください。
テストを行うコードは以下のようになります。普通のユニットテストと同じく、コードを保存するフォルダは「src/test/java」の方です。「src/main/java」に保存するとテストだけで使うアノテーションやクラスでエラーが発生するので注意しましょう。
package blog.tsuchiya.tutorial.step7.controller;
import static org.springframework.security.test.web.servlet.setup.SecurityMockMvcConfigurers.*;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.*;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.security.test.context.support.WithMockUser;
import org.springframework.test.web.servlet.MockMvc;
import org.springframework.test.web.servlet.setup.MockMvcBuilders;
import org.springframework.web.context.WebApplicationContext;
@SpringBootTest
class MainControllerTest {
private MockMvc mockMvc;
@Autowired
private WebApplicationContext webApplicationContext;
@BeforeEach
void setup() {
// @formatter:off
mockMvc = MockMvcBuilders
.webAppContextSetup(webApplicationContext)
// Spring Securityの設定をテストに反映させるために必要
.apply(springSecurity()).build(); }
// @formatter:on
@Test
@DisplayName("ADNIN権限のユーザで/adminにアクセス")
// 擬似的にADMINロールを持つadminがログインしている状態にする
@WithMockUser(username = "admin", roles = { "ADMIN" })
void accessWithAdminToAdmin() throws Exception {
// /adminはADMINロールを持つユーザなら正常に表示する
// @formatter:off
this.mockMvc.perform(get("/admin"))
.andExpect(view().name("admin"))
.andExpect(status().isOk());
// @formatter:on
}
@Test
@DisplayName("USER権限のユーザで/adminにアクセス")
// 擬似的にUSERロールを持つuserがログインしている状態にする
@WithMockUser(username = "user", roles = { "USER" })
void accessWithUserToAdmin() throws Exception {
// ADMINロールを持たないユーザはアクセス権を持たない
this.mockMvc.perform(get("/admin")).andExpect(status().isForbidden());
}
}
Spring Securityを使った認証/認可をしているURLをテストする際、ポイントとなるのは次の2点です。
- MockMvcのインスタンスを作る際、Spring Securityを有効にする
- @WithMockUserで擬似的なログイン状態を作る
Spring Securityを有効にする
サンプルコードのsetup()でMockMvcのインスタンスを生成していますが、Spring Securityを有効にした状態にするためには、明示的に設定を行う必要があります。
mockMvc = MockMvcBuilders
.webAppContextSetup(webApplicationContext)
// Spring Securityの設定をテストに反映させるために必要
.apply(springSecurity()).build(); }
コメントにも書いてあるとおり、apply(springSecurity())が肝です。applyメソッドをSecurityMockMvcConfigurers.springSecurity()を引数として実行しないと、MockMvcでperformメソッドを実行した際、Spring Securityが有効になりません。
仮にapplyメソッドを実行しないでMockMvcを作ると、Spring Securityで設定したアクセス制限が有効にならず、どのURLでも無条件でのアクセスが可能になってしまいます。
@WithMockUserで擬似的にログインする
@WithMockUserをテストメソッドにつけると、指定したユーザIDとロールを持つユーザがログインした状態でのテストを行えます。
@WithMockUser(username = "admin", roles = { "ADMIN" })
例えばaccessWithAdminToAdminメソッドには上記のアノテーションを付けていますが、これはユーザIDがadminでロールにADMINを持つユーザがログインしている状態を擬似的に作ります。そのため、Spring Securityの設定でロールにADMINをもつユーザしかアクセスを許可していない/adminへのアクセスが成功しているのです。
一方、accessWithUserToAdminではUSER権限を持つユーザがログインしている状態を擬似的に作り出した上で/adminにアクセスしています。USER権限では/adminへのアクセスはできないので、performした結果はHTTPステータス403(Forbidden)です。
status().isForbidden()はあまり使わないと思いますが、HTTPステータスが403であるかどうかを検証するMatcherです。
まとめ:Spring Securityのテストは難しくない
今回はかなり短めの記事となりました。MockMvcを使える知識があるのなら、Spring Securityの認証/認可を有効にしてのテストを行うのはそんなに難しくないと思います。
- MockMvcをapply(springSecurity())を使って作成する
- @WithMockUserで擬似的なログイン状態を作る
これだけです。
前回の記事と今回の記事を理解しておけば、Spring Bootのユニットテストはだいたいかけるようになっているはず。
今回の記事でSpring Boot入門を終わりたいと思います。ここまで読んでいただき、ありがとうございました。
開発環境の設定から各種実装方法、テストまで一通り説明してきました。全部の記事を読んでもらえたのなら、最低限のアプリケーションを作る知識は出来上がっていると思います。ぜひ仕事やオリジナルアプリの作成に活用してください。
この記事やこれまでの説明でわからないところがあったら、Twitterやお問い合わせフォームから連絡をください。記事ごとのコメント欄も開放しているので、そちらでも大丈夫です。
コメント