Developer-kit unit-test-security-authorization
Provides patterns for unit testing Spring Security with `@PreAuthorize`, `@Secured`, `@RolesAllowed`. Validates role-based access control and authorization policies. Use when testing security configurations and access control logic.
install
source · Clone the upstream repo
git clone https://github.com/giuseppe-trisciuoglio/developer-kit
Claude Code · Install into ~/.claude/skills/
T=$(mktemp -d) && git clone --depth=1 https://github.com/giuseppe-trisciuoglio/developer-kit "$T" && mkdir -p ~/.claude/skills && cp -r "$T/plugins/developer-kit-java/skills/unit-test-security-authorization" ~/.claude/skills/giuseppe-trisciuoglio-developer-kit-unit-test-security-authorization && rm -rf "$T"
manifest:
plugins/developer-kit-java/skills/unit-test-security-authorization/SKILL.mdsource content
Unit Testing Security and Authorization
Overview
This skill provides patterns for unit testing Spring Security authorization logic using
@PreAuthorize, @Secured, @RolesAllowed, and custom permission evaluators. It covers testing role-based access control (RBAC), expression-based authorization, custom permission evaluators, and verifying access denied scenarios without full Spring Security context.
When to Use
Use this skill when:
- Testing
and@PreAuthorize
method-level security@Secured - Testing role-based access control (RBAC)
- Testing custom permission evaluators
- Verifying access denied scenarios
- Testing authorization with authenticated principals
- Want fast authorization tests without full Spring Security context
Instructions
Follow these steps to test Spring Security authorization:
1. Set Up Security Testing Dependencies
Add spring-security-test to your test dependencies:
<dependency> <groupId>org.springframework.security</groupId> <artifactId>spring-security-test</artifactId> <scope>test</scope> </dependency>
2. Enable Method Security in Test Configuration
@Configuration @EnableMethodSecurity class TestSecurityConfig { }
3. Test with @WithMockUser
@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 Custom Permission Evaluators
@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. Validate Security is Active
If tests pass unexpectedly, add this assertion to verify security is enforced:
@Test void shouldRejectUnauthorizedWhenSecurityEnabled() { assertThatThrownBy(() -> service.deleteUser(1L)) .isInstanceOf(AccessDeniedException.class); }
Quick Reference
| Annotation | Description | Example |
|---|---|---|
| Pre-invocation authorization | |
| Post-invocation authorization | |
| Simple role-based security | |
| JSR-250 standard | |
| Test annotation | |
Examples
Basic @PreAuthorize
Test
@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); }
Expression-Based Security Test
@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(); }
Validation tip: If a security test passes unexpectedly, verify that
is active on the test configuration — a missing annotation causes all@EnableMethodSecuritychecks to be bypassed silently.@PreAuthorize
See references/basic-testing.md for more basic patterns and references/advanced-authorization.md for complex expressions and custom evaluators.
Best Practices
- Use
for setting authenticated user context@WithMockUser - Test both allow and deny cases for each security rule
- Test with different roles to verify role-based decisions
- Test expression-based security comprehensively
- Mock external dependencies (permission evaluators, etc.)
- Test anonymous access separately from authenticated access
- Use
in configuration for method-level security@EnableGlobalMethodSecurity
Common Pitfalls
- Forgetting to enable method security in test configuration
- Not testing both allow and deny scenarios
- Testing framework code instead of authorization logic
- Not handling null authentication in tests
- Mixing authentication and authorization tests unnecessarily
Constraints and Warnings
- Method security requires proxy:
works via proxies; direct method calls bypass security@PreAuthorize
: Must be enabled for@EnableGlobalMethodSecurity
,@PreAuthorize
to work@Secured- Role prefix: Spring adds "ROLE_" prefix automatically; use
nothasRole('ADMIN')hasRole('ROLE_ADMIN') - Authentication context: Security context is thread-local; be careful with async tests
limitations: Creates a simple Authentication; complex auth scenarios need custom setup@WithMockUser- SpEL expressions: Complex SpEL in
can be difficult to debug; test thoroughly@PreAuthorize - Performance impact: Method security adds overhead; consider security at layer boundaries
References
Setup and Configuration
- references/setup.md - Maven/Gradle dependencies and security configuration
Testing Patterns
- references/basic-testing.md - Basic patterns for
,@PreAuthorize
, MockMvc testing, and parameterized tests@Secured
Advanced Topics
- references/advanced-authorization.md - Expression-based authorization, custom permission evaluators, SpEL expressions
Complete Examples
- references/complete-examples.md - Before/after examples showing transition from manual to declarative security