关于 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启用方法级安全性
常见问题
- 忘记在测试配置中启用方法安全性
- 没有同时测试允许和拒绝的场景
- 测试框架代码而非授权逻辑
- 未处理测试中的空身份验证
- 不必要地混合身份验证测试和授权测试
限制与警告
- 方法安全性需要代理:
@PreAuthorize通过代理生效;直接调用方法会绕过安全检查 @EnableGlobalMethodSecurity:必须启用此项,@PreAuthorize和@Secured才能生效- 角色前缀:Spring 会自动添加 "ROLE_" 前缀;使用
hasRole('ADMIN'),而不是hasRole('ROLE_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 - 展示从手动安全机制过渡到声明式安全机制的前后对比示例
评论
支持 Markdown