Spring Security升级后自定义MethodSecurityExpressionRoot无法加载问题
问题背景
将Spring Boot从2.3.0.RELEASE升级至2.7.8、Spring Security从5.7.6升级至5.8.1后,仅替换@EnableGlobalMethodSecurity(prePostEnabled = true)为@EnableMethodSecurity,运行Spock单元测试时抛出异常:
Caused by: org.springframework.expression.spel.SpelEvaluationException: EL1004E: Method call: Method internalUser(java.lang.String) cannot be found on type org.springframework.security.access.expression.method.MethodSecurityExpressionRoot
排查发现自定义的CustomMethodSecurityExpressionRoot未被使用,默认的MethodSecurityExpressionRoot被加载,且CustomMethodSecurityExpressionHandler中的createSecurityExpressionRoot方法未被调用。
问题原因
Spring Security 5.7+引入的@EnableMethodSecurity底层配置逻辑发生变化:它默认使用MethodSecurityConfiguration而非旧版的GlobalMethodSecurityConfiguration,原有的自定义MethodSecurityExpressionHandler注册方式不再被自动识别,导致自定义表达式根无法生效。
解决方案
修改MethodSecurityConfig,通过MethodSecurityCustomizer显式注册自定义表达式处理器,让Spring Security加载并使用你的CustomMethodSecurityExpressionRoot。
修改后的MethodSecurityConfig.java
@EnableMethodSecurity public class MethodSecurityConfig { @Autowired private ApplicationContext context; @Bean public MethodSecurityCustomizer methodSecurityCustomizer() { // 为方法安全配置指定自定义表达式处理器 return configurer -> configurer.expressionHandler(customExpressionHandler()); } @Bean public CustomMethodSecurityExpressionHandler customExpressionHandler() { CustomMethodSecurityExpressionHandler handler = new CustomMethodSecurityExpressionHandler(); handler.setApplicationContext(context); return handler; } }
额外优化建议
- 移除
CustomMethodSecurityExpressionHandler中无意义的构造代码:
// 删除这段无用代码 public CustomMethodSecurityExpressionHandler() { String stopHere = "stopHere"; }
- 修正
CustomMethodSecurityExpressionRoot中setThis方法的访问权限(改为public,确保Spring Security能正常调用):
@Override public void setThis(Object target) { this.target = target; }
验证
重新运行Spock单元测试,此时Spring Security会触发CustomMethodSecurityExpressionHandler的createSecurityExpressionRoot方法,使用自定义的CustomMethodSecurityExpressionRoot,internalUser等自定义方法可被SpEL表达式正确识别。
内容的提问来源于stack exchange,提问作者Jonathan

