mcpField
U
unit-test-security-authorization

unit-test-security-authorization

github.com/giuseppe-trisciuoglio测试与质量3653.7k 次安装7 周前更新
代码仓库

关于 unit-test-security-authorization

这项技能介绍如何在不加载完整安全上下文的情况下测试 Spring Security 授权规则,适用于 Java 开发者。内容涵盖基于角色的检查、方法安全注解、自定义权限评估器及拒绝访问场景。

主要功能

  1. 01测试 `@PreAuthorize` 等注解
  2. 02用 `@WithMockUser` 模拟用户
  3. 03覆盖基于角色的允许与拒绝情形
  4. 04测试自定义权限评估器
  5. 05检查方法安全配置

适用场景

  1. 01验证仅管理员可调用的服务方法
  2. 02测试按用户角色设定的访问规则
  3. 03检查基于资源所有者的权限判断
  4. 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.md
basic-testing.md
complete-examples.md
setup.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")
@RolesAllowedJSR-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。

最佳实践

  1. 使用 @WithMockUser 设置经过身份验证的用户上下文
  2. 测试允许和拒绝两种情况,覆盖每条安全规则
  3. 使用不同角色进行测试,验证基于角色的决策
  4. 全面测试基于表达式的安全性
  5. 模拟外部依赖项(权限评估器等)
  6. 将匿名访问测试与经过身份验证的访问测试分开
  7. 在配置中使用 @EnableGlobalMethodSecurity 启用方法级安全性

常见问题

  • 忘记在测试配置中启用方法安全性
  • 没有同时测试允许和拒绝的场景
  • 测试框架代码而非授权逻辑
  • 未处理测试中的空身份验证
  • 不必要地混合身份验证测试和授权测试

限制与警告

  • 方法安全性需要代理:@PreAuthorize 通过代理生效;直接调用方法会绕过安全检查
  • @EnableGlobalMethodSecurity:必须启用此项,@PreAuthorize 和 @Secured 才能生效
  • 角色前缀:Spring 会自动添加 "ROLE_" 前缀;使用 hasRole('ADMIN'),而不是 hasRole('ROLE_ADMIN')
  • 身份验证上下文:安全上下文是线程本地的;异步测试时需谨慎
  • @WithMockUser 的限制:它会创建简单的 Authentication;复杂的身份验证场景需要自定义设置
  • SpEL 表达式:@PreAuthorize 中复杂的 SpEL 表达式可能难以调试;请充分测试
  • 性能影响:方法安全性会增加开销;可考虑在层边界处实施安全检查

参考资料

设置与配置
测试模式
高级主题
完整示例

评论

支持 Markdown