Spring Security ACL能否与WebSecurityExpressionHandler配合实现路由权限控制?
首先明确:Spring Security ACL完全支持路由级(HttpSecurity层面)的权限控制,并不是只能用在方法级。你的配置不生效,主要是两个关键细节没处理对:
1. Web安全表达式里路径变量的引用方式错了
在@PreAuthorize注解中,#ID能直接对应方法参数,但HttpSecurity的access()表达式上下文和方法级不一样——默认不会把路径变量自动暴露成表达式变量。你之前写的#ID其实是个不存在的变量,传递给hasPermission的是null或者错误值,导致生成的ObjectIdentity和数据库里存储的ACL记录不匹配,所以readAclsById才找不到对应条目。
怎么改?
Spring Security 5.5.x(正好对应你用的Spring Boot 2.5.5)的WebSecurityExpressionRoot提供了getPathVariable(String name)方法,直接用它拿路径变量就行。把你的路由规则改成这样:
.antMatchers(HttpMethod.PUT, "/api/user/users/{ID}") .access("isAuthenticated() and hasPermission(getPathVariable('ID'), 'xxx.xxx.xxx.xxx.xxx.UserDto', 'write')")
注意占位符名称{ID}要和getPathVariable里的参数完全一致,大小写也别错。
2. WebSecurityExpressionHandler的Bean配置有冗余
你的webExpressionHandler方法里,明明已经注入了AclPermissionEvaluator,结果又手动调用aclPermissionEvaluator()创建新实例——这会导致Web层用的PermissionEvaluator和方法级用的不是同一个实例(如果aclPermissionEvaluator()是个@Bean方法的话),很可能出现缓存、数据源配置不一致的问题。
修复后的配置:
@Bean public DefaultWebSecurityExpressionHandler webExpressionHandler(AclPermissionEvaluator aclPermissionEvaluator) { final DefaultWebSecurityExpressionHandler webSecurityExpressionHandler = new DefaultWebSecurityExpressionHandler(); // 直接用注入的实例,别自己新建 webSecurityExpressionHandler.setPermissionEvaluator(aclPermissionEvaluator); return webSecurityExpressionHandler; }
额外要检查的点
- 类型匹配:如果你的UserDto的ID是Long类型,要确认
getPathVariable('ID')返回的是Long而不是String,必要的话可以在表达式里转类型:T(Long).valueOf(getPathVariable('ID')) - 调试参数:可以在
AclPermissionEvaluator的hasPermission方法里打个断点,看看传入的identifier值是不是和数据库里acl_entry表的object_id_identity字段完全一致,这能快速定位是不是参数不匹配的问题。
内容的提问来源于stack exchange,提问作者M364M4N cro

