unit-test-security-authorization について
完全なセキュリティコンテキストを読み込まずに Spring Security の認可ルールをテストするスキルです。Java 開発者向けに、ロール判定、メソッドセキュリティのアノテーション、カスタム権限評価器、アクセス拒否のテスト方法を説明します。
主な機能
- 01`@PreAuthorize` などのアノテーションをテスト
- 02`@WithMockUser` によるユーザーの再現
- 03ロール別の許可・拒否ケースの確認
- 04カスタム権限評価器のテスト
- 05メソッドセキュリティ設定の確認
活用シーン
- 01管理者限定のサービスメソッドを検証
- 02ユーザーロールに応じたアクセスルールをテスト
- 03リソース所有者に基づく権限判定を確認
- 04未認証・未許可の呼び出しが拒否されることを確認
インストール
コマンドをコピーしてターミナルで実行します。
npx skills add https:/ / github.com/ giuseppe-trisciuoglio/ developer-kit --skill unit-test-security-authorizationインストール後、Claude Code で/unit-test-security-authorizationを入力して読み込まれたか確認します。
同梱ファイル
SKILL.md と一緒にインストールされる参考資料やスクリプト。
references/advanced-authorization.mdbasic-testing.mdcomplete-examples.mdsetup.md詳細
単体テストでのセキュリティと認可
概要
このスキルでは、@PreAuthorize、@Secured、@RolesAllowed、カスタム権限評価器を使用して、Spring Security の認可ロジックを単体テストするパターンを紹介します。ロールベースのアクセス制御(RBAC)、式ベースの認可、カスタム権限評価器のテストや、Spring Security のコンテキスト全体を使用せずにアクセス拒否のシナリオを検証する方法を扱います。
使用する場面
次の場合にこのスキルを使用します。
@PreAuthorizeと@Securedによるメソッドレベルセキュリティをテストする- ロールベースのアクセス制御(RBAC)をテストする
- カスタム権限評価器をテストする
- アクセス拒否のシナリオを検証する
- 認証済みプリンシパルによる認可をテストする
- Spring Security のコンテキスト全体を使用せず、高速に認可テストを行いたい
手順
以下の手順で Spring Security の認可をテストします。
1. セキュリティテスト用の依存関係を設定する
テストの依存関係に spring-security-test を追加します。
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-test</artifactId>
<scope>test</scope>
</dependency>
2. テスト設定でメソッドセキュリティを有効にする
@Configuration
@EnableMethodSecurity
class TestSecurityConfig { }
3. @WithMockUser を使用してテストする
@Test
@WithMockUser(roles = "ADMIN")
void shouldAllowAdminAccess() {
assertThatCode(() -> service.deleteUser(1L))
.doesNotThrowAnyException();
}
@Test
@WithMockUser(roles = "USER")
void shouldDenyUserAccess() {
assertThatThrownBy(() -> service.deleteUser(1L))
.isInstanceOf(AccessDeniedException.class);
}
4. カスタム権限評価器をテストする
@Test
void shouldGrantPermissionToOwner() {
Authentication auth = new UsernamePasswordAuthenticationToken(
"alice", null, List.of(new SimpleGrantedAuthority("ROLE_USER"))
);
Document doc = new Document(1L, "Test", new User("alice"));
boolean result = evaluator.hasPermission(auth, doc, "WRITE");
assertThat(result).isTrue();
}
5. セキュリティが有効であることを検証する
テストが予期せず成功する場合は、以下のアサーションを追加してセキュリティが適用されていることを確認します。
@Test
void shouldRejectUnauthorizedWhenSecurityEnabled() {
assertThatThrownBy(() -> service.deleteUser(1L))
.isInstanceOf(AccessDeniedException.class);
}
クイックリファレンス
| アノテーション | 説明 | 例 |
|---|---|---|
@PreAuthorize | 呼び出し前の認可 | @PreAuthorize("hasRole('ADMIN')") |
@PostAuthorize | 呼び出し後の認可 | @PostAuthorize("returnObject.owner == authentication.name") |
@Secured | シンプルなロールベースのセキュリティ | @Secured("ROLE_ADMIN") |
@RolesAllowed | JSR-250 標準 | @RolesAllowed({"ADMIN", "MANAGER"}) |
@WithMockUser | テスト用アノテーション | @WithMockUser(roles = "ADMIN") |
例
基本的な @PreAuthorize テスト
@Service
public class UserService {
@PreAuthorize("hasRole('ADMIN')")
public void deleteUser(Long userId) {
// delete logic
}
}
// Test
@Test
@WithMockUser(roles = "ADMIN")
void shouldAllowAdminToDeleteUser() {
assertThatCode(() -> service.deleteUser(1L))
.doesNotThrowAnyException();
}
@Test
@WithMockUser(roles = "USER")
void shouldDenyUserFromDeletingUser() {
assertThatThrownBy(() -> service.deleteUser(1L))
.isInstanceOf(AccessDeniedException.class);
}
式ベースのセキュリティテスト
@PreAuthorize("#userId == authentication.principal.id")
public UserProfile getUserProfile(Long userId) {
// get profile
}
// For custom principal properties, use @WithUserDetails with a custom UserDetailsService
@Test
@WithUserDetails("alice")
void shouldAllowUserToAccessOwnProfile() {
assertThatCode(() -> service.getUserProfile(1L))
.doesNotThrowAnyException();
}
検証のヒント:セキュリティテストが予期せず成功する場合は、テスト設定で
@EnableMethodSecurityが有効になっていることを確認してください。このアノテーションがないと、すべての@PreAuthorizeチェックが気付かれないまま回避されます。
基本的なパターンについては references/basic-testing.md、複雑な式やカスタム評価器については references/advanced-authorization.md を参照してください。
ベストプラクティス
- 認証済みユーザーのコンテキストを設定するには
@WithMockUserを使用する - 各セキュリティルールについて 許可と拒否の両方のケースをテストする
- ロールに基づく判断を確認するために 異なるロールでテストする
- 式ベースのセキュリティを網羅的にテストする
- 外部依存関係(権限評価器など)をモックする
- 認証済みアクセスとは分けて 匿名アクセスをテストする
- メソッドレベルセキュリティの設定には
@EnableGlobalMethodSecurityを使用する
よくある落とし穴
- テスト設定でメソッドセキュリティを有効にし忘れる
- 許可と拒否の両方のシナリオをテストしない
- 認可ロジックではなく、フレームワークのコードをテストする
- テストで null 認証を処理しない
- 認証テストと認可テストを不必要に混在させる
制約と注意事項
- メソッドセキュリティにはプロキシが必要です:
@PreAuthorizeはプロキシを介して動作します。メソッドを直接呼び出すとセキュリティが回避されます @EnableGlobalMethodSecurity:@PreAuthorizeと@Securedを動作させるには有効にする必要があります- ロールのプレフィックス:Spring は自動的に「ROLE_」プレフィックスを追加します。
hasRole('ROLE_ADMIN')ではなくhasRole('ADMIN')を使用します - 認証コンテキスト:セキュリティコンテキストはスレッドローカルです。非同期テストでは注意してください
@WithMockUserの制限:単純な Authentication を作成します。複雑な認証シナリオにはカスタム設定が必要です- SpEL 式:
@PreAuthorize内の複雑な SpEL はデバッグが難しい場合があります。十分にテストしてください - パフォーマンスへの影響:メソッドセキュリティはオーバーヘッドを追加します。レイヤーの境界でのセキュリティも検討してください
参考資料
セットアップと設定
- references/setup.md - Maven/Gradle の依存関係とセキュリティ設定
テストパターン
- references/basic-testing.md -
@PreAuthorize、@Secured、MockMvc テスト、パラメーター化テストの基本パターン
高度なトピック
- references/advanced-authorization.md - 式ベースの認可、カスタム権限評価器、SpEL 式
完全な例
- references/complete-examples.md - 手動セキュリティから宣言的セキュリティへの移行を示す変更前と変更後の例