SpEL表达式中使用注解属性遇阻:@PreAuthorize扩展注解问题
问题描述
想要创建一个带参数的注解来扩展@PreAuthorize,编写的代码如下:
@Inherited @PreAuthorize("@levelService.verify(#level)") public @interface LevelAuthGuard { int level() default 0; }
但SpEL表达式中的#level(或#level())无法访问到注解的level属性,对此存在以下困惑:
- SpEL可以直接使用方法参数;
- 类级别的SpEL注解在编译时初始化,而
level属性同样是编译时定义且不可修改,和直接传入@PreAuthorize的SpEL字符串情况一致。
需求:在不使用自定义AOP Bean/配置的情况下解决该问题,同时需要明白为什么无法访问注解属性。
问题解答
一、为什么无法访问注解属性?
你在元注解@PreAuthorize里写#level的时候,SpEL的上下文是被注解的方法/类的执行上下文,根本不是你自定义的LevelAuthGuard注解的上下文。Spring Security处理@PreAuthorize时,只会解析当前方法的参数、Spring容器里的Bean,不会自动把你的自定义注解属性注入到SpEL上下文里。说白了,#level在当前上下文中完全是个未定义的变量,Spring根本不知道它指向的是你自定义注解的level属性。
二、不使用自定义AOP的解决方法
直接用Spring SpEL的@符号就能拿到当前注解的属性,不用搞自定义AOP,具体改法如下:
- 把
@PreAuthorize的SpEL表达式改成通过@target()获取当前方法上的LevelAuthGuard注解实例,直接访问它的level属性:
@Inherited @PreAuthorize("@levelService.verify(@target(com.yourpackage.LevelAuthGuard).level)") public @interface LevelAuthGuard { int level() default 0; }
记得把com.yourpackage换成你实际的包路径。
- 如果这个注解要用到类上,就用
@within()代替@target(),这样类和方法级别的注解都能生效:
@Inherited @PreAuthorize("@levelService.verify(@within(com.yourpackage.LevelAuthGuard).level)") public @interface LevelAuthGuard { int level() default 0; }
原理很简单:@target()和@within()是SpEL的内置表达式,用来获取当前执行上下文对应的注解实例,这样就能直接拿到注解的level属性,完全符合你的需求,不需要额外的AOP配置。
内容的提问来源于stack exchange,提问作者Vadym Borys
相关产品推荐
相关产品推荐

