2016-07-27 77 views
3

我遇到了在Spring Boot應用程序中通過Java配置設置ACL的問題。我已經創建了一個小型項目來重現問題。Spring Boot中的ACL安全性

我嘗試了幾種不同的方法。我遇到的第一個問題是EhCache,在我修正了這個問題後(我認爲我做了),我再也無法登錄了,看起來所有的數據都消失了。

有4類不同的配置:

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

所有@PreAuthorize@PostAuthorize註釋是否按預期工作,除了hasPermission

控制器擁有4個端點:一個是用戶,一個用於管理,一個公共和最後一個,給了我頭疼@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中以編程方式配置的示例?

+0

我有點被你的問題百思不得其解。是@PostAuthorize(「hasPermission(returnObject,'administration')」)'不是唯一的問題嗎? – xnakos

+0

@xnakos,Idealy數據需要被緩存。當管理工作時應該很容易通過DB – 5er

+0

@ 5er設置讀寫權限您是否可以共享示例數據庫條目以授權用戶使用數據對象執行特定操作?我真的需要幫助[類似的主題](http://stackoverflow.com/questions/41872192/access-is-always-denied-in-spring-security-denyallpermissionevaluator?noredirect=1#comment72145872_41872192)。 –

回答

8

找不到數據的原因有點棘手。只要在配置中定義了一個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觸發的。這個BeanPostProcessorDataSourceInitializedPublisher)是作爲spring-boot的Hibernate/JPA自動配置的一部分創建的(請參閱org.springframework.boot.autoconfigure.orm.jpa.HibernateJpaAutoConfigurationorg.springframework.boot.autoconfigure.orm.jpa.DataSourceInitializedPublisher和和org.springframework.boot.autoconfigure.jdbc.DataSourceInitializer)。

通常在創建(嵌入)DataSourceDataSourceInitializedPublisher創建和一切工作正常,但通過定義一個定製MethodSecurityExpressionHandler正常bean創建順序會改變。 正如您配置@EnableGlobalMethodSecurity,your're自動導入GlobalMethodSecurityConfiguration

彈簧安全相關的bean是在早期創建的。由於你的MethodSecurityExpressionHandler需要一個DataSource作爲ACL的東西,並且與彈簧安全相關的beans需要你的自定義MethodSecurityExpressionHandler,所以DataSource比平時早創建;實際上它的創建時間很早,因此尚未創建彈簧啓動的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彈簧引導的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豆。 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; 
    } 

} 

你需要做的最後一件事是使汽車大衆getId()方法。

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

標準ObjectIdentityRetrievalStrategy將尋找一個公共方法「的getId()」試圖確定ACL權限評估期間對象的身份的時候。

(請注意,我基於ACLConfig4我的回答。)

+0

謝謝@彼得,它的工作就像它應該的。這似乎是最大的問題是數據庫設置。 MethodSecurityExpressionHandler bean和Car中缺少的公共方法完全是我的疏忽:(順便說一句,我並不知道我使用* data.sql *的方式不正確,指定的地方在哪裏?你是怎麼知道的?從哪個文檔中獲得?謝謝。 – 5er

+0

I實際上通過深入解析您的問題來了解它,但事實證明它也在文檔中提到。請參閱http://docs.spring.io/spring-boot/docs/1.4.0。RELEASE /參考/ htmlsingle /#HOWTO初始化-A-數據庫使用彈簧-JDBC – Pieter