【发布时间】:2022-11-11 01:41:08
【问题描述】:
我创建了一个注释,用于验证某些安全方面是否正确。
例如,@RequireClientCertificate,带有一个 Aspect 实现RequireClientCertificateAspect,用于验证是否确实将正确的 HTTP 标头传递到 Spring REST 控制器。
如果实际加载了RequireClientCertificateAspect,即如果在@ComponentScan() 的某处提到了它的包,这完全可以正常工作。
但是,如果有人忘记将这个包添加到@ComponentScan,或者切面被移动到另一个包,或者有人(不小心)从@ComponentScan 中删除了包,则切面bean 没有加载,切面完全没有应用。
我在一个公共库中有这个注释,由多个微服务共享,因此其中一个微服务很容易意外出错。在这种情况下,不会检查客户端证书。
问题:如果使用了@RequireClientCertificate 注释,我如何强制执行它对应的Aspect 实现也被加载?
简化使用示例:
@Controller
@RequestMapping(value = "/v1.0", produces = MediaType.APPLICATION_JSON_VALUE)
@RequireClientCertificate
public class SomeApiController {
@ResponseBody
@PostMapping("/get-token/")
public ResponseEntity<Token> getToken() {
return ResponseEntity.ok(...get token...);
}
}
方面的简化版本:
@Aspect
@Component
public class RequireClientCertificateAspect {
@Around("execution(* (@RequireClientCertificate *).*(..))")
public Object requireClientCertificateAspectImplementation(ProceedingJoinPoint joinPoint) throws Throwable {
... verify request header ...
try {
return joinPoint.proceed();
finally {
... some other things I need to check ...
}
}
}
我尝试过/考虑过的事情:
我可以通过向接口添加带有初始化程序的静态字段来检测注释的“使用情况”。例如:
@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
public @interface RestFactoryGatewaySecurityContext {
static public final boolean dummy = SomeClass.checkAspectIsLoaded();
}
但是,这样的初始化程序很早就被调用了,而且我认为 Spring DI 在那个阶段还没有“启动并运行”得足够远,以至于我什至可以可靠地确定是否加载了方面 bean。
另一种选择是使用@Autowired 在主应用程序类上显式注入RequireClientCertificateAspect bean。如果不知何故 bean 不在组件扫描中,这将阻止 Spring 实例化应用程序。
所以这确实有效,但需要有人明确添加这个“虚拟”自动连线,除了有点“丑陋”之外,它本身很容易忘记。
【问题讨论】:
-
我建议创建一些集成测试并检查该方面是否有效。
-
当然。但是这些测试在公共库中。即使我会在微服务中为它编写一个额外的测试,我如何确保测试的 ComponentScan 等于主程序的呢?当然,有人可能再次忘记在新的微服务中编写这样的测试。
-
[更新]对不起,我误读了您的评论,并阅读了单元测试。是的,还需要集成测试,但我宁愿保持安全,以防有人忘记编写它们(假设它“正常工作”,因为注释肯定在那里......)。