我在通过Spring Boot应用程序中的Java配置设置ACL时遇到问题。我创建了一个小项目来重现这些问题。
我尝试了几种不同的方法。我遇到的第一个问题是EhCache,在我解决了这个问题之后(我以为是),我再也无法登录了,看来所有数据都消失了。
有4种具有不同配置的类:
ACLConfig1.class ACLConfig2.class ACLConfig3.class ACLConfig4.class
除@PreAuthorize和@PostAuthorize之外,所有和注释均按预期工作hasPermission。
@PreAuthorize
@PostAuthorize
hasPermission
控制器拥有4个端点:一个端点用于用户,一个端点用于Admin,一个公共端点和最后一个使我头疼的端点 @PostAuthorize("hasPermission(returnObject,'administration')")
@PostAuthorize("hasPermission(returnObject,'administration')")
我很确定DB中的插入是正确的。此类是四堂之一,我尝试过的最后一堂:
@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中以编程方式配置它?
找不到数据的原因有点棘手。MethodSecurityExpressionHandler在配置中定义bean后,数据库表中就没有数据。这是因为您的data.sql文件未执行。
MethodSecurityExpressionHandler
data.sql
在解释为什么data.sql不执行之前,我首先要指出您没有按预期使用该文件。
data.sql在初始化hibernate状态后由spring- boot执行,并且通常仅包含DML语句。您data.sql包含DDL(模式)语句和DML(数据)语句。这是不理想的,因为您的某些DDL语句与hibernate的hibernate.hbm2ddl.auto行为发生冲突(请注意,在使用嵌入式时,spring- boot使用“ create-drop” DataSource)。您应该将DDL语句放入,schema.sql并将DML语句放入data.sql。手动定义所有表时,您应该禁用hibernate.hbm2ddl.auto(添加spring.jpa.hibernate.ddl- auto=none到applciation.properties)。
hibernate.hbm2ddl.auto
DataSource
schema.sql
spring.jpa.hibernate.ddl- auto=none
applciation.properties
话虽如此,让我们看一下为什么data.sql不执行。
的执行data.sql是通过触发的,通过ApplicationEvent触发的BeanPostProcessor。这BeanPostProcessor(DataSourceInitializedPublisher)作为弹簧启动的hibernate/ JPA自动配置(见的一部分创建org.springframework.boot.autoconfigure.orm.jpa.HibernateJpaAutoConfiguration, org.springframework.boot.autoconfigure.orm.jpa.DataSourceInitializedPublisher和org.springframework.boot.autoconfigure.jdbc.DataSourceInitializer)。
ApplicationEvent
BeanPostProcessor
DataSourceInitializedPublisher
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。
@EnableGlobalMethodSecurity
GlobalMethodSecurityConfiguration
与spring安全相关的bean是在早期创建的。由于您MethodSecurityExpressionHandler需要DataSourceACL内容,而与spring- security相关的bean则需要您的custom MethodSecurityExpressionHandler,因此DataSource它的创建要比平时更早;实际上,它创建得太早了,DataSourceInitializedPublisher还没有创建spring- boot 。在DataSourceInitializedPublisher以后创建的,但因为它没有注意到的创建DataSource豆,它也不会触发的执行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。
spring.jpa.hibernate.ddl-auto=none
定义自己的DataSource弹簧靴时,DataSourceAutoConfiguration通常会退出,但是如果您想确保也可以将其排除(可选)。
DataSourceAutoConfiguration
@SpringBootConfiguration @EnableAutoConfiguration(exclude = DataSourceAutoConfiguration.class) @ComponentScan @EnableCaching public class Application { public static void main(String[] args) { SpringApplication.run(Application.class, args); } }
这应该可以解决您的“无数据”问题。但是,要使所有功能按预期工作,您需要再进行2次修改。
首先,您应该只定义一个MethodSecurityExpressionHandlerbean。当前,您正在定义2个MethodSecurityExpressionHandlerbean。Spring- security不知道要使用哪个安全性,而是(静静地)使用它自己的内部安全性MethodSecurityExpressionHandler。请参阅org.springframework.security.config.annotation.method.configuration.GlobalMethodSecurityConfiguration#setMethodSecurityExpressionHandler。
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; } }
您需要做的最后一件事是使getId()Car中的方法公开。
getId()
@Entity public class Car { //... public long getId() { return id; } //... }
ObjectIdentityRetrievalStrategy当在ACL权限评估期间尝试确定对象的身份时,该标准将查找公共方法“ getId()”。
ObjectIdentityRetrievalStrategy
(请注意,我的回答基于ACLConfig4。)
ACLConfig4