2016-07-27 74 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