Spring:模拟安全增强服务时保留@PreAuthorize逻辑的方法
你遇到的问题本质是Mockito直接生成的mock对象跳过了Spring Security的代理增强——毕竟两者都基于CGLIB,但Mockito的mock是直接生成代理类,不会触发Spring Security对@PreAuthorize的处理逻辑。要同时实现业务逻辑Mock和安全校验保留,得让Spring来主导代理创建,而不是直接用Mockito手动mock。
下面是具体的实现步骤和示例:
核心思路
不要用Mockito.mock(MyService.class)手动创建mock,而是借助Spring的@MockBean注解:
- Spring会先为
MyService创建带有Spring Security权限校验的代理实例(处理@PreAuthorize) - 再将代理实例中的业务方法替换为Mockito的mock实现
- 最终我们注入的是经过Security增强+业务逻辑Mock的服务对象
具体实现步骤
1. 配置测试类
给测试类加上Spring Security方法级安全的注解,以及Spring上下文相关注解:
// 轻量配置:只加载必要的上下文,无需启动完整Spring Boot应用 @ContextConfiguration(classes = {MyService.class, MethodSecurityConfig.class}) @EnableMethodSecurity // Spring Security 6+用这个,5.x用@EnableGlobalMethodSecurity(prePostEnabled = true) public class MyServiceSecurityTest { // 让Spring创建代理后,自动mock业务方法 @MockBean private MyService myService; // 注入经过Spring Security增强的代理对象 @Autowired private MyService securedMyService; // 测试用例... } // 单独的方法安全配置类(可复用) @Configuration @EnableMethodSecurity public class MethodSecurityConfig { }
2. 编写权限测试用例
用Spring Security的测试工具模拟不同角色的用户,验证权限控制逻辑:
@Test @WithMockUser(roles = "ADMIN") // 模拟ADMIN角色用户 void adminOnlyMethod_shouldAllowAccess() { // Mock业务方法的返回值(无需关心真实业务逻辑) when(myService.adminOnlyMethod()).thenReturn("mocked admin content"); // 调用经过权限增强的服务方法 String result = securedMyService.adminOnlyMethod(); // 验证:权限校验通过,业务方法被调用 assertEquals("mocked admin content", result); verify(myService).adminOnlyMethod(); } @Test @WithMockUser(roles = "USER") // 模拟普通USER角色用户 void adminOnlyMethod_shouldDenyAccess() { // 调用方法时,应该抛出权限拒绝异常 assertThrows(AccessDeniedException.class, () -> securedMyService.adminOnlyMethod()); // 验证:权限校验失败,业务方法未被执行 verifyNoInteractions(myService); }
关键细节说明
- 为什么@MockBean有效?:
@MockBean是Spring Test提供的注解,它会让Spring先完成所有代理增强(包括Spring Security的@PreAuthorize处理),再把目标对象的方法替换成Mockito的mock实现,完美兼顾了安全校验和业务Mock。 - 不要直接用@Autowired+Mockito.spy:这种方式虽然能保留原始代理,但spy会保留真实业务逻辑,不符合你"无需涉及业务逻辑"的测试需求。
- 轻量测试选项:如果不想加载完整Spring Boot上下文,用
@ContextConfiguration指定必要的类即可,比@SpringBootTest启动更快。
内容的提问来源于stack exchange,提问作者VB_
相关产品推荐
相关产品推荐

