【发布时间】:2017-06-11 20:23:07
【问题描述】:
我已经在我的 Spring Boot 应用程序中配置了 ACL。 ACL配置如下:
@Configuration
@ComponentScan(basePackages = "com.company")
@EnableGlobalMethodSecurity(prePostEnabled = true, securedEnabled = true)
public class ACLConfigration extends GlobalMethodSecurityConfiguration {
@Autowired
DataSource dataSource;
@Bean
public EhCacheBasedAclCache aclCache() {
return new EhCacheBasedAclCache(aclEhCacheFactoryBean().getObject(), permissionGrantingStrategy(), aclAuthorizationStrategy());
}
@Bean
public EhCacheFactoryBean aclEhCacheFactoryBean() {
EhCacheFactoryBean ehCacheFactoryBean = new EhCacheFactoryBean();
ehCacheFactoryBean.setCacheManager(aclCacheManager().getObject());
ehCacheFactoryBean.setCacheName("aclCache");
return ehCacheFactoryBean;
}
@Bean
public EhCacheManagerFactoryBean aclCacheManager() {
return new EhCacheManagerFactoryBean();
}
@Bean
public DefaultPermissionGrantingStrategy permissionGrantingStrategy() {
ConsoleAuditLogger consoleAuditLogger = new ConsoleAuditLogger();
return new DefaultPermissionGrantingStrategy(consoleAuditLogger);
}
@Bean
public AclAuthorizationStrategy aclAuthorizationStrategy() {
return new AclAuthorizationStrategyImpl(new SimpleGrantedAuthority("ROLE_ACL_ADMIN"));
}
@Bean
public LookupStrategy lookupStrategy() {
return new BasicLookupStrategy(dataSource, aclCache(), aclAuthorizationStrategy(), new ConsoleAuditLogger());
}
@Bean
public JdbcMutableAclService aclService() {
return new JdbcMutableAclService(dataSource, lookupStrategy(), aclCache());
}
@Bean
public DefaultMethodSecurityExpressionHandler defaultMethodSecurityExpressionHandler() {
return new DefaultMethodSecurityExpressionHandler();
}
@Override
public MethodSecurityExpressionHandler createExpressionHandler() {
DefaultMethodSecurityExpressionHandler expressionHandler = defaultMethodSecurityExpressionHandler();
expressionHandler.setPermissionEvaluator(new AclPermissionEvaluator(aclService()));
expressionHandler.setPermissionCacheOptimizer(new AclPermissionCacheOptimizer(aclService()));
return expressionHandler;
}
}
参考资料:
安全配置如下:
@Configuration
@EnableWebSecurity
public class CustomSecurityConfiguration extends WebSecurityConfigurerAdapter {
@Bean
public AuthenticationEntryPoint entryPoint() {
return new LoginUrlAuthenticationEntryPoint("/authenticate");
}
@Override
protected void configure(HttpSecurity http) throws Exception {
http
.csrf()
.disable()
.authorizeRequests()
.antMatchers("/authenticate/**").permitAll()
.anyRequest().fullyAuthenticated()
.and().requestCache().requestCache(new NullRequestCache())
.and().addFilterBefore(authenticationFilter(), CustomUsernamePasswordAuthenticationFilter.class);
}
@Override
public void configure(WebSecurity web) throws Exception {
web.ignoring().antMatchers(HttpMethod.OPTIONS, "/**");
}
@Bean
public CustomUsernamePasswordAuthenticationFilter authenticationFilter()
throws Exception {
CustomUsernamePasswordAuthenticationFilter authenticationFilter = new CustomUsernamePasswordAuthenticationFilter();
authenticationFilter.setUsernameParameter("username");
authenticationFilter.setPasswordParameter("password");
authenticationFilter.setFilterProcessesUrl("/authenticate");
authenticationFilter.setAuthenticationSuccessHandler(new CustomAuthenticationSuccessHandler());
authenticationFilter.setAuthenticationFailureHandler(new CustomAuthenticationFailureHandler());
authenticationFilter.setAuthenticationManager(authenticationManagerBean());
return authenticationFilter;
}
@Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
}
我的CustomAuthenticationProvider班级:
@Component
public class CustomAuthenticationProvider implements AuthenticationProvider {
@Autowired
private UsersService usersService;
@Override
public Authentication authenticate(Authentication authentication)
throws AuthenticationException {
String username = authentication.getName();
String password = authentication.getCredentials().toString();
User user = usersService.findOne(username);
if(user != null && usersService.comparePassword(user, password)){
return new UsernamePasswordAuthenticationToken(
user.getUsername(),
user.getPassword(),
AuthorityUtils.commaSeparatedStringToAuthorityList(
user.getUserRoles().stream().collect(Collectors.joining(","))));
} else {
return null;
}
}
@Override
public boolean supports(Class<?> authentication) {
return authentication.equals(UsernamePasswordAuthenticationToken.class);
}
}
这是我的CustomUsernamePasswordAuthenticationToken:
public class CustomUsernamePasswordAuthenticationFilter extends UsernamePasswordAuthenticationFilter {
@Override
public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response)
throws AuthenticationException {
if(!request.getMethod().equals("POST"))
throw new AuthenticationServiceException(String.format("Authentication method not supported: %s", request.getMethod()));
try {
CustomUsernamePasswordAuthenticationForm form = new ObjectMapper().readValue(request.getReader(), CustomUsernamePasswordAuthenticationForm.class);
String username = form.getUsername();
String password = form.getPassword();
if(username == null)
username = "";
if(password == null)
password = "";
UsernamePasswordAuthenticationToken token = new UsernamePasswordAuthenticationToken(username, password);
setDetails(request, token);
return getAuthenticationManager().authenticate(token);
} catch (IOException exception) {
throw new CustomAuthenticationException(exception);
}
}
private class CustomAuthenticationException extends RuntimeException {
private CustomAuthenticationException(Throwable throwable) {
super(throwable);
}
}
}
除了上述之外,我还有CustomAuthenticationFailureHandler、CustomAuthenticationSuccessHandler、CustomNoRedirectStrategy 和CustomUsernamePasswordAuthenticationForm,为了这个问题的长度,我跳过了它们。
我正在使用可以在here 找到的 MySQL 架构。
我正在向我的 acl 相关表中添加如下条目:
INSERT INTO acl_class VALUES (1, com.company.project.domain.users.User)
INSERT INTO acl_sid VALUES (1, 1, "demo")
(我有一个用户名为demo)
INSERT INTO acl_object_identity VALUES (1, 1, 1, NULL, 1, 0)
INSERT INTO acl_entry VALUES (1, 1, 1, 1, 1, 1, 1, 1)
但我得到的只是:
Denying user demo permission 'READ' on object com.company.project.domain.users.User@4a49e9b4
在我的
@PostFilter("hasPermission(filterObject, 'READ')")
我怀疑这里有几个问题:
-
hasPermission表达式:我已将其替换为 'READ' 和 '1',但没有达到任何程度。 - 我的数据库条目不正确
- 我没有实现自定义权限评估程序。这是必需的,还是
expressionHandler.setPermissionEvaluator(new AclPermissionEvaluator(aclService()));足够?
更新
使用@PostFilter的示例方法:
@RequestMapping(method = RequestMethod.GET)
@PostFilter("hasPermission(filterObject, 'READ')")
List<User> find(@Min(0) @RequestParam(value = "limit", required = false, defaultValue = "10") Integer limit,
@Min(0) @RequestParam(value = "page", required = false, defaultValue = "0") Integer page,
@RequestParam(value = "email", required = false) String email,
@RequestParam(value = "firstName", required = false) String firstName,
@RequestParam(value = "lastName", required = false) String lastName,
@RequestParam(value = "userRole", required = false) String userRole) {
return usersService.find(
limit,
page,
email,
firstName,
lastName,
userRole);
}
更新 #2:
问题现在反映了有关身份验证/授权/ACL 的所有设置。
更新 #3:
我现在非常接近解决这个问题,唯一剩下的就是解决这个问题:
如果有人可以帮助我解决这个问题,我终于可以写下我为解决这个问题所经历的事情了。
【问题讨论】:
-
是@PostFilter 在实现接口的公共上的方法吗?
-
不,它在
@RestController或@Controller上。我真的怀疑数据库条目,或者不存在的组件。 -
这个方法怎么样,你把@PostFilter注解放在哪里了?您的服务器日志中是否有任何 Stacktrace?
-
我不是很喜欢 ACL,但您是否混淆了
@PreAuthorize和@PostFilter的用法?见concretepage.com/spring/spring-security/… -
并非如此。正如官方文档(以及您共享的链接)所建议的那样,
@PostFilter根据经过身份验证的用户是否有权执行表达式中所述的操作来过滤返回对象。这正是我想要达到的目标。问题是,无论数据库中的 ACL 条目如何,每个对象都会被过滤。
标签: java spring spring-boot spring-security acl