Spring Boot中的ACL安全性 [英] ACL security in Spring Boot

查看:1493
本文介绍了Spring Boot中的ACL安全性的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

I am having issues setting up ACL through Java config in a Spring Boot application. I have created one small project to reproduce the issues.

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

I have tried a few different approaches. First issue I had was with EhCache, and after I fixed that (I assume I did) I couldn't login any more, and it looks like all the data is gone.

有4个类具有不同的配置:

There are 4 classes with different configurations:

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

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

All @PreAuthorize and @PostAuthorize annotations are working as expected, except hasPermission.

控制器拥有4个端点:一个用于用户,一个用于管理员,一个用于公共,最后一个让我头疼 @PostAuthorize(hasPermission( returnObject,'administration'))

Controller holds 4 endpoints: one for User, one for Admin, one Public and the last one which gives me headache @PostAuthorize("hasPermission(returnObject,'administration')")

我很确定DB中的插入是正确的。这个类是四个中的一个,也是我尝试过的最后一个类:

I am pretty sure that inserts in DB are correct. This class is one of four, the last one that I have tried:

@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中以编程方式配置它的示例?

What am I missing here? Why I have no data if I use ACLConfig3.class or ACLConfig4.class. Is there any example on how this should be configured programmatically in Spring Boot?

推荐答案

您没有数据的原因有点找出答案很棘手。只要在配置中定义 MethodSecurityExpressionHandler bean,数据库表中就没有数据。这是因为你的 data.sql 文件没有被执行。

The reason why you have no data was a bit tricky to find out. As soon as you define a MethodSecurityExpressionHandler bean in your config, there is no data in the database tables. This is because your data.sql file isn't executed.

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

Before explaining why data.sql isn't executed I'd first like to point out that you're not using the file as intended.

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

data.sql is executed by spring-boot after hibernate has been initialized and normally only contains DML statements. Your data.sql contains both DDL (schema) statements and DML (data) statements. This isn't ideal as some of your DDL statements clash with hibernate's hibernate.hbm2ddl.auto behaviour (note that spring-boot uses 'create-drop' when an embedded DataSource is being used). You should put your DDL statements in schema.sql and your DML statements in data.sql. As you're manually defining all tables you should disable hibernate.hbm2ddl.auto (by adding spring.jpa.hibernate.ddl-auto=none to applciation.properties).

话虽如此,让我们来看看为什么 data.sql 未执行。

That being said, let's take a look at why data.sql isn't executed.

执行 data.sql 是通过 BeanEostProcessor 触发的 ApplicationEvent 触发的。这个 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 )。

The execution of data.sql is triggered via an ApplicationEvent that's fired via a BeanPostProcessor. This BeanPostProcessor (DataSourceInitializedPublisher) is created as a part of spring-boot's Hibernate/JPA auto configuration (see org.springframework.boot.autoconfigure.orm.jpa.HibernateJpaAutoConfiguration, org.springframework.boot.autoconfigure.orm.jpa.DataSourceInitializedPublisher and org.springframework.boot.autoconfigure.jdbc.DataSourceInitializer).

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

Normally the DataSourceInitializedPublisher is created before the (embedded) DataSource is created and everything works as expected but by defining a custom MethodSecurityExpressionHandler the normal bean creation order alters. As you've configured @EnableGlobalMethodSecurity, your're automatically importing GlobalMethodSecurityConfiguration.

早期创建与spring-security相关的bean。由于您的 MethodSecurityExpressionHandler 需要 DataSource 来获取ACL资源,而与spring-security相关的bean需要您的自定义 MethodSecurityExpressionHandler DataSource 比平时更早创建;事实上,它是在Spring-boot的 DataSourceInitializedPublisher 的早期创建的。
稍后会创建 DataSourceInitializedPublisher ,但由于它没有注意到创建 DataSource bean,它也不会触发 data.sql 的执行。

spring-security related beans are created early on. As your MethodSecurityExpressionHandler requires a DataSource for the ACL stuff and the spring-security related beans require your custom MethodSecurityExpressionHandler, the DataSource is created earlier than usual; in fact it's created so early on that spring-boot's DataSourceInitializedPublisher isn't created yet. The DataSourceInitializedPublisher is created later on but as it didn't notice the creation of a DataSource bean, it also doesn't trigger the execution of data.sql.

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

So long story short: the security configuration alters the normal bean creation order which results in data.sql not being loaded.

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

I guess that fixing the bean creation order would do the trick, but as I don't now how (without further experimentation) I propose the following solution: manually define your DataSource and take care of data initialization.

@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

As your data.sql file contains all DDL required by your application you can disable hibernate.hbm2ddl.auto. Add spring.jpa.hibernate.ddl-auto=none to applciation.properties.

定义自己的 DataSource spring-boot的 DataSourceAutoConfiguration 通常会退出但是如果你想要的话确保你也可以排除它(可选)。

When defining your own DataSource spring-boot's DataSourceAutoConfiguration normally back's out but if you want to be sure you can also exclude it (optional).

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

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

}

这应修复你的'没有数据'问题。但是为了让一切按预期工作,你需要再做两次修改。

This should fix your 'no data' problem. But in order to get everything working as expected you need to make 2 more modifications.

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

First of all, you should only define one MethodSecurityExpressionHandler bean. Currently you're defining 2 MethodSecurityExpressionHandler beans. Spring-security won't know which one to use and will (silently) use it's own internal MethodSecurityExpressionHandler instead. See 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()方法。

The last thing you need to do is make the getId() method in Car public.

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

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

The standard ObjectIdentityRetrievalStrategy will look for a public method 'getId()' when trying to determine an object's identity during ACL permission evaluation.

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

这篇关于Spring Boot中的ACL安全性的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆