Java – ACL security in spring boot
I encountered a problem setting ACL through Java configuration in spring boot application I created a small project to reproduce the problem
I've tried several different methods The first problem I encountered was ehcache. After I repaired it (I assumed I did it), I could no longer log in. It seemed that all the data disappeared
There are four classes with different configurations:
ACLConfig1.class ACLConfig2.class ACLConfig3.class ACLConfig4.class
All @ preauthorize and @ postauthorize annotations work as expected, except for haspermission
The controller has four endpoints: one for user, one for admin, one for public, and the last one gives me a headache @ postauthorize ("haspermission (returnobject, 'administration')")
I'm pretty sure the insertion in DB is correct This class is one of the four and the last one I 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; } }
What did I miss here? If I use aclconfig3 Class or why I don't have data aclconfig4 class. Is there an example of how to configure spring boot programmatically?
Solution
It's a bit tricky not to find the reason why you don't have data As long as the methodsecurityexpressionhandler bean is defined in the configuration, there is no data in the database table This is because of your data SQL file not executed
Explaining why the data is not executed Before SQL, I'll first point out that you didn't use the file as expected
After hibernate is initialized, spring Sboot executes data SQL, usually contains only DML statements Your data SQL contains DDL (schema) statements and DML (data) statements This is not ideal because some DDL statements are related to hibernate hbm2ddl. Auto behavior conflicts (note that when using an embedded datasource, spring boot uses' create drop ') You should put your DDL statements in schema. XML In SQL, put the DML statement in data In SQL Hibernate should be disabled when you manually define all tables hbm2ddl. Auto (by adding spring.jpa.hibernate.ddl-auto = none to applciation.properties)
Having said that, let's see why we didn't execute data sql.
data. SQL execution is triggered by an applicationevent triggered by beanpostprocessor This beanpostprocessor (datasourceinitialized publisher) was created as part of Hibernate / JPA auto configuration for spring boot (see org.springframework.boot.autoconfigure.orm.jpa.hibernatejpaautoconfiguration, org.springframework.boot.autoconfigure.orm.jpa.datasourceinitializedpublisher and org. Springframework. Boot. Autoconfigure. JDBC. Datasourceinitializer)
Usually, datasourceinitializedpublisher is created before creating (embedding) datasource, and everything works as expected, but by defining a custom methodsecurityexpressionhandler, the normal bean creation order will change After configuring @ enablegrobalmethodsecurity, you will automatically import globalmethodsecurityconfiguration
Spring - Security - related beans are created early Because your methodsecurityexpressionhandler needs the data source of ACL resources, and spring security related beans need your custom methodsecurityexpressionhandler, the datasource is created earlier than usual; In fact, it was created early in spring boot's datasourceinitialized publisher Datasourceinitializedpublisher will be created later, but because it does not notice the creation of datasource bean, it will not trigger data SQL execution
Long story short: security configuration changes the normal bean creation order, resulting in data SQL was not loaded
I think fixing the bean creation order can solve this problem, but I don't know how (no further experiment) I propose the following solution: manually define your datasource and be responsible for 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(); } }
Due to data The SQL file contains all the DDLS required by the application, so hibernate. SQL can be disabled hbm2ddl. auto. Add spring jpa. hibernate. DDL auto = none added to application properties.
When defining your own datasource, the datasourceautoconfiguration of spring boot usually exits, but you can also exclude it if you want to be sure (optional)
@SpringBootConfiguration @EnableAutoConfiguration(exclude = DataSourceAutoConfiguration.class) @ComponentScan @EnableCaching public class Application { public static void main(String[] args) { SpringApplication.run(Application.class,args); } }
This should solve your "no data" problem But in order for everything to work as expected, you need to make two more modifications
First, you should define only one methodsecurityexpressionhandler bean Currently, you are defining 2 methodsecurityexpressionhandler beans Spring security doesn't know which one to use, but (silently) uses its own internal methodsecurityexpressionhandler 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; } }
The last thing you need to do is set the getid () method in the car to public
@Entity public class Car { //... public long getId() { return id; } //... }
When trying to determine the identity of an object during ACL permission evaluation, the standard objectidentityretrievalstrategy looks for the public method 'getid()'
(please note that my answer is based on aclconfig4.)