我按照这个指南使用acl设置安全性,它工作得很好。在那之后,我还试图在WebSecurityConfigurerAdapter中实现acl。因此,我设置了一个bean,它将创建DefaultWebSecurityExpressionHandler:
@Bean
public DefaultWebSecurityExpressionHandler webExpressionHandler(AclPermissionEvaluator aclPermissionEvaluator) {
final DefaultWebSecurityExpressionHandler webSecurityExpressionHandler = new DefaultWebSecurityExpressionHandler();
AclPermissionEvaluator permissionEvaluator = aclPermissionEvaluator();
webSecurityExpressionHandler.setPermissionEvaluator(permissionEvaluator);
return webSecurityExpressionHandler;
}
和我将它应用到HttpSecurity在我的WebSecurityConfigurerAdapter:
@Override
protected void configure(HttpSecurity http) throws Exception {
http.csrf().disable()
.authorizeRequests()
.expressionHandler(webExpressionHandlerWithACL)
.antMatchers(HttpMethod.PUT, "/api/user/users/{ID}").access("isAuthenticated() and hasPermission(#ID, 'xxx.xxx.xxx.xxx.xxx.UserDto', 'write')")
.anyRequest().authenticated()
.and()
.exceptionHandling().authenticationEntryPoint(authenticationEntryPoint)
.and()
.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS);
http.addFilterBefore(filter, UsernamePasswordAuthenticationFilter.class);
}
但是这行不通。这个完全相同的表达式在@PreAuthorize标记中也可以使用,但在这里却不能。我对它进行了调试,发现准确的故障点在Spring提供的JdbcAclServiceImpl类中,方法如下:
@Override
public Map<ObjectIdentity, Acl> readAclsById(List<ObjectIdentity> objects, List<Sid> sids)
throws NotFoundException {
Map<ObjectIdentity, Acl> result = this.lookupStrategy.readAclsById(objects, sids);
// Check every requested object identity was found (throw NotFoundException if
// needed)
for (ObjectIdentity oid : objects) {
if (!result.containsKey(oid)) {
throw new NotFoundException("Unable to find ACL information for object identity '" + oid + "'");
}
}
return result;
}
即使提供的参数与我使用@ preauthorization标签相同,即使我可以用调试器看到它,result.containsKey(oid)
也无法找到acl,并且我看到oid
应该匹配它。
我的问题是:Spring Security ACL甚至应该为了保护路由而工作,还是只用于保护方法?
btw,我使用的是Spring boot 2.5.5
我找到问题了。类AclPermissionEvaluator
有一个方法public boolean hasPermission(Authentication authentication, Serializable targetId, String targetType, Object permission)
被调用。如果我使用它作为webExpressionHandler,那么Serializable targetId
总是一个字符串。但要让它起作用,它需要很长。因此,一个解决方案是扩展AclPermissionEvaluator
并覆盖有问题的方法,以便它将字符串转换为Long,然后它工作。别忘了实际使用自定义的AclPermissionEvaluator
.