响应式Spring中服务类@PreAuthorize注解在测试用例中未生效
核心根因
- 测试用
@InjectMockKs直接构造了FooServiceImpl的原始实例,这个实例根本没有经过Spring的AOP代理。Spring Security的方法权限拦截完全依赖为容器内的Bean生成代理对象,在方法调用前插入校验逻辑,直接调用原始对象的方法时,所有安全注解都会被完全跳过,自然不会抛出AccessDeniedException。 - 测试上下文没有将
FooServiceImpl注册为Spring Bean:当前测试仅导入了安全配置类,容器中压根不存在这个服务实例,就算去掉手动注入的逻辑,也不会为这个类生成带权限拦截的代理对象。 - 之前尝试添加的
@EnableGlobalMethodSecurity确实是多余配置:这个注解是Servlet/MVC栈的方法安全配置,和WebFlux栈使用的@EnableReactiveMethodSecurity是两套独立实现,混合添加反而可能引发Bean冲突,确实不需要额外配置。
修复方案
- 替换
@InjectMockKs的手动实例化逻辑,改为从Spring容器中获取被代理后的服务Bean - 将
FooServiceImpl加入测试上下文的注册范围,确保容器能正常为其生成代理对象
调整后的测试代码参考:@ExtendWith(SpringExtension::class, MockKExtension::class) @Import(TestSecurityConfig::class) // 显式注册被测服务类,纳入容器管理 @ContextConfiguration(classes = [FooServiceImpl::class, TestSecurityConfig::class]) internal class FooServiceImplTest { // 注入容器生成的代理Bean,而非手动构造的原始实例 @Autowired lateinit var fooService: FooService @Test fun shouldDeny() { runBlocking { // 精准匹配预期抛出的权限异常类型 assertThrows<AccessDeniedException> { fooService.bar() } } } } - 注意版本适配:你的被测方法是Kotlin
suspend协程方法,需要Spring Security 6.0及以上版本才能原生支持协程方法的权限切面,低版本的Reactive方法安全组件识别不了suspend函数的切点,也会导致注解不生效。
测试过程中如果需要Mock被测类的依赖,应该用
@MockBean/@SpyBean的方式注册Mock对象,不要直接用@InjectMockKs手动构造整个被测实例,不然会完全绕过容器的代理增强逻辑。
内容的提问来源于stack exchange,提问作者Stuck
相关产品推荐
相关产品推荐

