unit-test-security-authorization
giuseppe-trisciuoglio/developer-kit
Unit test Spring Security authorization with @PreAuthorize, @Secured, and role-based access control patterns.
What is unit-test-security-authorization?
Provides patterns for testing Spring Security method-level authorization annotations and role-based access control (RBAC). Use this skill when validating @PreAuthorize, @Secured, @RolesAllowed decorators and custom permission evaluators in unit tests.
- Test @PreAuthorize and @Secured method-level security annotations
- Validate role-based access control (RBAC) with @WithMockUser
- Test custom permission evaluators and expression-based authorization
- Verify access denied scenarios for unauthorized principals
- Test @PostAuthorize and @RolesAllowed (JSR-250) annotations
- Confirm security context is active and authorization is enforced
How to install unit-test-security-authorization
npx skills add https://github.com/giuseppe-trisciuoglio/developer-kit --skill unit-test-security-authorization- spring-security-test dependency in test scope
- @EnableMethodSecurity or @EnableGlobalMethodSecurity in test configuration
- JUnit 5 or compatible test framework
- Basic understanding of Spring Security annotations
How to use unit-test-security-authorization
- 1.Add spring-security-test to test dependencies
- 2.Enable method security in test configuration with @EnableMethodSecurity
- 3.Use @WithMockUser(roles="ROLE_NAME") to set authenticated user context
- 4.Write test cases for both allow and deny scenarios
- 5.Use @WithUserDetails for expression-based security with custom principals
- 6.Assert AccessDeniedException is thrown for unauthorized access
- 7.Verify security is active by testing unauthenticated access
Use cases
- Testing admin-only operations like deleteUser() with different roles
- Validating expression-based security rules that check user ownership
- Testing custom permission evaluators for document or resource access
- Verifying that unauthorized users receive AccessDeniedException
- Testing role-based workflows with multiple role combinations
- Spring developers writing unit tests for secured services
- QA engineers validating authorization logic
- Security-focused teams implementing role-based access control
- Developers migrating from manual to declarative security
unit-test-security-authorization FAQ
Use @WithMockUser(roles="ADMIN") on test methods to set the authenticated user's roles. Write separate test methods for each role to verify both allow and deny cases.
Verify that @EnableMethodSecurity is active on your test configuration. Without it, @PreAuthorize checks are silently bypassed. Also ensure you're calling the method through a proxy, not directly.
Create an Authentication object with UsernamePasswordAuthenticationToken, instantiate your permission evaluator, and call hasPermission(auth, resource, permission) directly in the test.
Yes, use @WithUserDetails with a custom UserDetailsService to provide a principal with the properties your SpEL expression needs.
@Secured uses simple role names (ROLE_ADMIN), while @PreAuthorize supports complex SpEL expressions. @PreAuthorize is more flexible and recommended for new code.
Full instructions (SKILL.md)
Source of truth, from giuseppe-trisciuoglio/developer-kit.
name: unit-test-security-authorization
description: 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.
allowed-tools: Read, Write, Bash, Glob, Grep
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
@PreAuthorizeand@Securedmethod-level security - 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
@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 |
|---|---|---|
@PreAuthorize | Pre-invocation authorization | @PreAuthorize("hasRole('ADMIN')") |
@PostAuthorize | Post-invocation authorization | @PostAuthorize("returnObject.owner == authentication.name") |
@Secured | Simple role-based security | @Secured("ROLE_ADMIN") |
@RolesAllowed | JSR-250 standard | @RolesAllowed({"ADMIN", "MANAGER"}) |
@WithMockUser | Test annotation | @WithMockUser(roles = "ADMIN") |
Examples
Basic @PreAuthorize Test
@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
@EnableMethodSecurityis active on the test configuration — a missing annotation causes all@PreAuthorizechecks to be bypassed silently.
See references/basic-testing.md for more basic patterns and references/advanced-authorization.md for complex expressions and custom evaluators.
Best Practices
- Use
@WithMockUserfor setting authenticated user context - 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
@EnableGlobalMethodSecurityin configuration for method-level security
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:
@PreAuthorizeworks via proxies; direct method calls bypass security @EnableGlobalMethodSecurity: Must be enabled for@PreAuthorize,@Securedto work- Role prefix: Spring adds "ROLE_" prefix automatically; use
hasRole('ADMIN')nothasRole('ROLE_ADMIN') - Authentication context: Security context is thread-local; be careful with async tests
@WithMockUserlimitations: Creates a simple Authentication; complex auth scenarios need custom setup- SpEL expressions: Complex SpEL in
@PreAuthorizecan be difficult to debug; test thoroughly - 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,@Secured, MockMvc testing, and parameterized tests
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
Related skills
More from giuseppe-trisciuoglio/developer-kit and the wider catalog.

unit-test-service-layer
Unit test service layer with Mockito: mock dependencies, verify interactions, test business logic in isolation.

unit-test-utility-methods
Test patterns for utility classes, static methods, and pure functions with edge case coverage.

unit-test-wiremock-rest-api
Unit test REST API integrations with WireMock stubs, request verification, and error simulation.

wiremock-standalone-docker
Run WireMock as a standalone Docker container to mock APIs for integration testing.

zod-validation-utilities
Type-safe validation schemas for APIs, forms, and config with Zod v4 and strong TypeScript inference.

api-review
Review Kubernetes API and CRD design quality for Kelos changes and proposals.