【问题标题】:ACL security in Spring BootSpring Boot 中的 ACL 安全性
【发布时间】:2016-12-01 06:46:05
【问题描述】:

我在 Spring Boot 应用程序中通过 Java 配置设置 ACL 时遇到问题。我创建了一个小项目来重现这些问题。

我尝试了几种不同的方法。我遇到的第一个问题是 EhCache,在我修复了这个问题之后(我假设我做到了)我无法再登录,而且看起来所有数据都消失了。

有4个不同配置的类:

ACLConfig1.class
ACLConfig2.class
ACLConfig3.class
ACLConfig4.class

所有@PreAuthorize 和@PostAuthorize 注释都按预期工作,hasPermission 除外。

Controller 拥有 4 个端点:一个用于 User,一个用于 Admin,一个用于 Public,最后一个让我头疼 @PostAuthorize("hasPermission(returnObject,'administration')")

我很确定数据库中的插入是正确的。这门课是四门课程之一,也是我尝试过的最后一门课程:

@Configuration
@EnableGlobalMethodSecurity(prePostEnabled = true, securedEnabled = true)
public class ACLConfig4 {

@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_ADMINISTRATOR"));
}

@Bean
public LookupStrategy lookupStrategy() {
    return new BasicLookupStrategy(dataSource, aclCache(), aclAuthorizationStrategy(), new ConsoleAuditLogger());
}

@Bean
public JdbcMutableAclService aclService() {
    JdbcMutableAclService service = new JdbcMutableAclService(dataSource, lookupStrategy(), aclCache());
    return service;
}

@Bean
public DefaultMethodSecurityExpressionHandler defaultMethodSecurityExpressionHandler() {
    return new DefaultMethodSecurityExpressionHandler();
}

@Bean
public MethodSecurityExpressionHandler createExpressionHandler() {
    DefaultMethodSecurityExpressionHandler expressionHandler = defaultMethodSecurityExpressionHandler();
    expressionHandler.setPermissionEvaluator(new AclPermissionEvaluator(aclService()));
    expressionHandler.setPermissionCacheOptimizer(new AclPermissionCacheOptimizer(aclService()));
    return expressionHandler;
}


}

我在这里缺少什么?如果我使用 ACLConfig3.class 或为什么我没有数据 ACLConfig4.class。有没有关于如何在 Spring Boot 中以编程方式配置它的示例?

【问题讨论】:

  • 你的问题让我有点困惑。 @PostAuthorize("hasPermission(returnObject,'administration')") 不是您唯一的问题吗?
  • @xnakos,理想的数据需要被缓存。当管理工作应该很容易通过 DB 设置读写权限
  • @5er 您能否分享示例数据库条目以授权用户对数据对象进行特定操作?我真的需要similar topic 的帮助。
  • 有没有什么好的链接可以用postgre在Spring Boot中实现ACL

标签: java spring-security spring-boot acl spring-cache


【解决方案1】:

您没有数据的原因有点棘手。一旦您在配置中定义了MethodSecurityExpressionHandler bean,数据库表中就没有数据了。这是因为你的data.sql 文件没有被执行。

在解释为什么不执行 data.sql 之前,我想先指出您没有按预期使用该文件。

data.sql 在 hibernate 初始化后由 spring-boot 执行,通常只包含 DML 语句。您的data.sql 包含 DDL(模式)语句和 DML(数据)语句。这并不理想,因为您的某些 DDL 语句与 hibernate 的 hibernate.hbm2ddl.auto 行为发生冲突(请注意,当使用嵌入式 DataSource 时,spring-boot 使用“create-drop”)。您应该将 DDL 语句放在 schema.sql 中,将 DML 语句放在 data.sql 中。当您手动定义所有表时,您应该禁用hibernate.hbm2ddl.auto(通过将spring.jpa.hibernate.ddl-auto=none 添加到applciation.properties)。

话虽如此,让我们看看为什么data.sql没有被执行。

data.sql 的执行是通过ApplicationEvent 触发的,而BeanPostProcessor 则通过BeanPostProcessor 触发。这个 BeanPostProcessor (DataSourceInitializedPublisher) 是作为 spring-boot 的 Hibernate/JPA 自动配置的一部分创建的(参见 org.springframework.boot.autoconfigure.orm.jpa.HibernateJpaAutoConfiguration、org.springframework.boot.autoconfigure.orm.jpa.DataSourceInitializedPublisher 和 org.springframework.boot.autoconfigure.jdbc.DataSourceInitializer)。

通常DataSourceInitializedPublisher 在创建(嵌入的)DataSource 之前创建,并且一切都按预期工作,但是通过定义自定义MethodSecurityExpressionHandler,正常的 bean 创建顺序会改变。 当您配置了@EnableGlobalMethodSecurity,您将自动导入GlobalMethodSecurityConfiguration。

spring-security 相关的 bean 是在早期创建的。由于您的MethodSecurityExpressionHandler 需要DataSource 用于ACL 内容,而与spring-security 相关的bean 需要您的自定义MethodSecurityExpressionHandler,因此DataSource 的创建时间比平时要早;事实上,它是在 spring-boot 的DataSourceInitializedPublisher 尚未创建的早期创建的。 DataSourceInitializedPublisher 是稍后创建的,但由于它没有注意到 DataSource bean 的创建,因此它也不会触发 data.sql 的执行。

长话短说:安全配置改变了正常的 bean 创建顺序,导致 data.sql 未加载。

我想修复 bean 创建顺序可以解决问题,但我现在不知道如何(没有进一步的实验)我提出以下解决方案:手动定义您的 DataSource 并处理数据初始化。

@Configuration
public class DataSourceConfig {
    @Bean
    public EmbeddedDatabase dataSource() {
        return new EmbeddedDatabaseBuilder().setType(EmbeddedDatabaseType.H2)
                 //as your data.sql file contains both DDL & DML you might want to rename it (e.g. init.sql)
                .addScript("classpath:/data.sql")
                .build();
    }
}

由于您的 data.sql 文件包含应用程序所需的所有 DDL,您可以禁用 hibernate.hbm2ddl.auto。将spring.jpa.hibernate.ddl-auto=none 添加到applciation.properties。

在定义您自己的DataSource 时,spring-boot 的DataSourceAutoConfiguration 通常会退出,但如果您想确保也可以排除它(可选)。

@SpringBootConfiguration
@EnableAutoConfiguration(exclude = DataSourceAutoConfiguration.class)
@ComponentScan
@EnableCaching
public class Application {

    public static void main(String[] args) {
        SpringApplication.run(Application.class, args);
    }

}

这应该可以解决您的“无数据”问题。但为了让一切按预期工作,您需要再进行 2 处修改。

首先,你应该只定义一个MethodSecurityExpressionHandler bean。目前您正在定义 2 个MethodSecurityExpressionHandler bean。 Spring-security 不知道要使用哪一个,而是会(默默地)使用它自己的内部MethodSecurityExpressionHandler。见org.springframework.security.config.annotation.method.configuration.GlobalMethodSecurityConfiguration#setMethodSecurityExpressionHandler。

@Configuration
@EnableGlobalMethodSecurity(prePostEnabled = true, securedEnabled = true)
public class MyACLConfig {

    //...
    @Bean
    public MethodSecurityExpressionHandler createExpressionHandler() {
        DefaultMethodSecurityExpressionHandler securityExpressionHandler = new DefaultMethodSecurityExpressionHandler();
        securityExpressionHandler.setPermissionEvaluator(new AclPermissionEvaluator(aclService()));
        securityExpressionHandler.setPermissionCacheOptimizer(new AclPermissionCacheOptimizer(aclService()));
        return securityExpressionHandler;
    }

}

您需要做的最后一件事是公开 Car 中的 getId() 方法。

@Entity
public class Car {
    //...    
    public long getId() {
        return id;
    }
    //...
}

标准ObjectIdentityRetrievalStrategy 在 ACL 权限评估期间尝试确定对象的身份时会查找公共方法“getId()”。

(请注意,我的答案基于ACLConfig4。)

【讨论】:

  • 谢谢@Pieter,它的工作方式应该如此。似乎最大的问题是数据库设置。 MethodSecurityExpressionHandler bean 和 Car 中缺少的公共方法完全是我的疏忽 :(。顺便说一句,我不知道我使用 data.sql 不正确,在哪里指定?你是怎么知道的?从哪个文档?谢谢。
  • 我实际上是通过深入研究试图解决您的问题的代码来学习它的,但事实证明文档中也提到了它。见docs.spring.io/spring-boot/docs/1.4.0.RELEASE/reference/…
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2018-10-25
  • 2021-07-14
  • 2016-05-14
  • 2017-07-02
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多