Unit tests Spring Security authorization with @PreAuthorize, @Secured, @RolesAllowed using @WithMockUser and custom evaluators. Validates RBAC, permissions, and access denied scenarios.
From developer-kit-javanpx claudepluginhub giuseppe-trisciuoglio/developer-kit --plugin developer-kit-javaThis skill is limited to using the following tools:
references/advanced-authorization.mdreferences/basic-testing.mdreferences/complete-examples.mdreferences/setup.mdSearches, retrieves, and installs Agent Skills from prompts.chat registry using MCP tools like search_skills and get_skill. Activates for finding skills, browsing catalogs, or extending Claude.
Searches prompts.chat for AI prompt templates by keyword or category, retrieves by ID with variable handling, and improves prompts via AI. Use for discovering or enhancing prompts.
Enables AI agents to execute x402 payments with per-task budgets, spending controls, and non-custodial wallets via MCP tools. Use when agents pay for APIs, services, or other agents.
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.
Use this skill when:
@PreAuthorize and @Secured method-level securityFollow these steps to test Spring Security authorization:
Add spring-security-test to your test dependencies:
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-test</artifactId>
<scope>test</scope>
</dependency>
@Configuration
@EnableMethodSecurity
class TestSecurityConfig { }
@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);
}
@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();
}
If tests pass unexpectedly, add this assertion to verify security is enforced:
@Test
void shouldRejectUnauthorizedWhenSecurityEnabled() {
assertThatThrownBy(() -> service.deleteUser(1L))
.isInstanceOf(AccessDeniedException.class);
}
| 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") |
@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);
}
@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.
@WithMockUser for setting authenticated user context@EnableGlobalMethodSecurity in configuration for method-level security@PreAuthorize works via proxies; direct method calls bypass security@EnableGlobalMethodSecurity: Must be enabled for @PreAuthorize, @Secured to workhasRole('ADMIN') not hasRole('ROLE_ADMIN')@WithMockUser limitations: Creates a simple Authentication; complex auth scenarios need custom setup@PreAuthorize can be difficult to debug; test thoroughly@PreAuthorize, @Secured, MockMvc testing, and parameterized tests