Spring 安全 ACL 应该与 WebSecurityExpressionHandler 一起使用吗?
Is Spring Security ACL supposed to be usable with WebSecurityExpressionHandler?
我按照 this guide 使用 ACL 设置安全性,效果很好。
之后我还尝试在 WebSecurityConfigurerAdapter 中实现 ACL。
所以我设置了一个 bean,它将创建一个 DefaultWebSecurityExpressionHandler:
@Bean
public DefaultWebSecurityExpressionHandler webExpressionHandler(AclPermissionEvaluator aclPermissionEvaluator) {
final DefaultWebSecurityExpressionHandler webSecurityExpressionHandler = new DefaultWebSecurityExpressionHandler();
AclPermissionEvaluator permissionEvaluator = aclPermissionEvaluator();
webSecurityExpressionHandler.setPermissionEvaluator(permissionEvaluator);
return webSecurityExpressionHandler;
}
我会将它应用到我的 WebSecurityConfigurerAdapter 中的 HttpSecurity:
@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提供的JdbcAclServiceImplclass下面的方法中:
@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;
}
即使提供的参数与我使用@PreAuthorize 标记时相同,result.containsKey(oid)
也无法在其中找到 acl,即使我可以使用调试器看到它并且我看到 oid
应该匹配它。
所以我的问题是:
Spring 安全 ACL 甚至应该用于保护路由,还是仅用于保护方法?
顺便说一句,我正在使用 Spring boot 2.5.5
我发现了问题。 class AclPermissionEvaluator
有一个方法 public boolean hasPermission(Authentication authentication, Serializable targetId, String targetType, Object permission)
被调用。如果我将它用作 webExpressionHandler,那么 Serializable targetId
总是一个字符串。但要让它起作用,它需要是一个 Long。因此,一种解决方案是扩展 AclPermissionEvaluator
并覆盖有问题的方法,以便它将 String 转换为 Long,然后它就可以工作了。不要忘记实际使用自定义 AclPermissionEvaluator
.
我按照 this guide 使用 ACL 设置安全性,效果很好。 之后我还尝试在 WebSecurityConfigurerAdapter 中实现 ACL。 所以我设置了一个 bean,它将创建一个 DefaultWebSecurityExpressionHandler:
@Bean
public DefaultWebSecurityExpressionHandler webExpressionHandler(AclPermissionEvaluator aclPermissionEvaluator) {
final DefaultWebSecurityExpressionHandler webSecurityExpressionHandler = new DefaultWebSecurityExpressionHandler();
AclPermissionEvaluator permissionEvaluator = aclPermissionEvaluator();
webSecurityExpressionHandler.setPermissionEvaluator(permissionEvaluator);
return webSecurityExpressionHandler;
}
我会将它应用到我的 WebSecurityConfigurerAdapter 中的 HttpSecurity:
@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提供的JdbcAclServiceImplclass下面的方法中:
@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;
}
即使提供的参数与我使用@PreAuthorize 标记时相同,result.containsKey(oid)
也无法在其中找到 acl,即使我可以使用调试器看到它并且我看到 oid
应该匹配它。
所以我的问题是: Spring 安全 ACL 甚至应该用于保护路由,还是仅用于保护方法?
顺便说一句,我正在使用 Spring boot 2.5.5
我发现了问题。 class AclPermissionEvaluator
有一个方法 public boolean hasPermission(Authentication authentication, Serializable targetId, String targetType, Object permission)
被调用。如果我将它用作 webExpressionHandler,那么 Serializable targetId
总是一个字符串。但要让它起作用,它需要是一个 Long。因此,一种解决方案是扩展 AclPermissionEvaluator
并覆盖有问题的方法,以便它将 String 转换为 Long,然后它就可以工作了。不要忘记实际使用自定义 AclPermissionEvaluator
.