我已经在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课:
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:
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我跳过这个问题的长度的缘故。
CustomAuthenticationFailureHandler
CustomAuthenticationSuccessHandler
CustomNoRedirectStrategy
CustomUsernamePasswordAuthenticationForm
我正在使用可以在此处找到的MySQL模式。
我将条目添加到我的ACL相关表中,如下所示:
INSERT INTO acl_class VALUES (1, com.company.project.domain.users.User) INSERT INTO acl_sid VALUES (1, 1, "demo")
(我有一个用户名为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
expressionHandler.setPermissionEvaluator(new AclPermissionEvaluator(aclService()));
更新资料
使用示例方法@PostFilter:
@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的所有设置。
这是期待已久的答案:
该文档清楚地描述了:
要使用hasPermission()表达式,必须在应用程序上下文中显式配置PermissionEvaluator。看起来像这样:
所以基本上我正在做我的AclConfiguration扩展GlobalMethodSecurityConfiguration:
AclConfiguration
GlobalMethodSecurityConfiguration
@Override protected MethodSecurityExpressionHandler createExpressionHandler() { DefaultMethodSecurityExpressionHandler expressionHandler = new DefaultMethodSecurityExpressionHandler(); expressionHandler.setPermissionEvaluator(new AclPermissionEvaluator(aclService())); expressionHandler.setPermissionCacheOptimizer(new AclPermissionCacheOptimizer(aclService())); return expressionHandler; }
Spring尚未对此进行处理!
我不得不分开AclConfig和GlobalMethodSecurityConfiguration。如果@Bean在后者中定义,则不会处理上述方法,这可能是一个错误(如果没有,欢迎您提供有关主题的任何说明)。
AclConfig
@Bean