如何将@PreAuthorize内的SecurityExpressionRoot传入自定义校验方法
实现方案
完全可以实现,不需要复杂扩展,Spring EL本身提供的内置变量就可以直接拿到你要的SecurityExpressionRoot实例。
方案一:直接传SpEL内置根对象(无额外配置,成本最低)
Spring在解析@PreAuthorize里的表达式时,会把MethodSecurityExpressionRoot(SecurityExpressionRoot的子类,针对方法级权限校验做了扩展)作为表达式的根对象,你可以直接通过SpEL内置的#root变量引用这个实例,直接作为参数传入你的自定义方法即可。
代码修改
- 调整Controller中
@PreAuthorize注解的参数:
public class TestController { private final PreAuthorizeChecker preAuthorizeChecker; @Autowired public TestController(PreAuthorizeChecker preAuthorizeChecker) { this.preAuthorizeChecker = preAuthorizeChecker; } @GetMapping(path = "/test") // 直接传入#root即可获取当前请求对应的SecurityExpressionRoot实例 @PreAuthorize("@preAuthorizeChecker.checkAccess(#root)") public ResponseEntity<Void> get() { return ResponseEntity.ok().build(); } }
- 调整校验类的方法参数(可选,建议用实际运行时类型避免类型转换问题):
运行时实际传入的是MethodSecurityExpressionRoot实例,它继承自SecurityExpressionRoot,你既可以直接用父类声明参数,也可以用子类声明来访问方法级安全的扩展能力:
@Component public class PreAuthorizeChecker { public boolean checkAccess(MethodSecurityExpressionRoot expressionRoot) { // 可以直接调用原生方法 boolean hasBasePerm = expressionRoot.hasAuthority("test:access"); // 可以获取当前认证信息做自定义校验 Authentication currentUser = expressionRoot.getAuthentication(); // 写你的自定义校验逻辑 return hasBasePerm && /* 自定义判断条件 */ true; } }
方案二:扩展原生Security表达式根(适合多规则场景)
如果你有大量自定义权限校验逻辑,不想每次在注解里写bean名称调用方法,可以直接扩展原生的MethodSecurityExpressionRoot,把你的校验方法注册为原生表达式方法,和hasAuthority()、hasRole()这类内置方法平级调用,不需要额外传根对象。
大致实现步骤:
- 自定义表达式根类继承
MethodSecurityExpressionRoot,在类中写你的checkAccess等自定义校验方法 - 自定义
MethodSecurityExpressionHandler,创建表达式实例时返回你自定义的根对象 - 在Spring Security的方法级安全配置中,注册你自定义的ExpressionHandler
配置完成后你可以直接在注解里写@PreAuthorize("checkAccess()"),方法内部可以直接调用所有内置权限方法,写法更简洁。
注意:不要尝试手动new SecurityExpressionRoot实例传入,这类实例没有绑定当前请求的认证上下文、方法参数、请求对象等信息,完全无法完成正常的权限校验。
内容的提问来源于stack exchange,提问作者Mateusz Maciejko
相关产品推荐
相关产品推荐

