ACL и OAuth для Spring Security: AclPermissionEvaluator перезаписывается DenyAllEvaluator
Поэтому у меня есть приложение SpringBoot с JavaConfig, использующее spring-security-acl (версия 3.2.7.RELEASE), следуя этому руководству http://krams915.blogspot.com/2011/01/spring-security-3-full-acl-tutorial.html кроме того, что я также использую OAuth. Проблема в том, что во время выполнения AclPermissionEvaluator всегда заменяется на DenyAllPermissionEvaluator.
TLDR; как заставить Spring-security-acl работать с OAuth2Authentication? для каждого требуется @EnableGlobalMethodSecurity, но в разных местах (acl в подклассе GlobalMethodSecurityConfiguration и OAuth2 в приложении). Тем не менее, вы не можете определить это дважды. Аналогично этому, но с OAuth2: как настроить Spring ACL без XML-файла)
pom.xml
<project>
<parent>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-parent</artifactId>
<!-- <version>Brixton.M4</version> -->
<version>Angel.SR3</version>
</parent>
<dependencies>
<!-- Spring-boot and cloud -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-config</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-eureka</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-oauth2</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-rest</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-core</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-config</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-acl</artifactId>
<!-- <version>4.1.0.BUILD-SNAPSHOT</version> -->
<!-- this is intentional to support non-numeric ID's e.g. UUIDs-->
</dependency>
<dependency>
<groupId>org.postgresql</groupId>
<artifactId>postgresql</artifactId>
<scope>runtime</scope>
<version>${postgres.version}</version>
</dependency>
</dependencies>
</project>
Приложение SpringBoot
@SpringBootApplication
@EnableEurekaClient
@EnableFeignClients(basePackages = "com.mycompany.mypackage.clients")
@EnableOAuth2Resource
@EnableGlobalMethodSecurity(prePostEnabled = true, securedEnabled = true)
public class Application {
public static void main(String[] args) throws Exception {
SpringApplication.run(Application.class, args);
}
}
Java Config
@Configuration
@Slf4j
@EnableGlobalMethodSecurity(prePostEnabled = true, securedEnabled = true)
public class AclSecurityConfig extends GlobalMethodSecurityConfiguration {
@Value("${spring.datasource.url}")
private String jdbcUrl;
@Value("${spring.datasource.username}")
private String username;
@Value("${spring.datasource.password}")
private String password;
@Value("${spring.datasource.driver-class-name}")
private String driverClass;
@Autowired
private ConversionService defaultConversionService;
@Override
protected MethodSecurityExpressionHandler createExpressionHandler() {
log.info("CreateExpressionHandler() got called");
DefaultMethodSecurityExpressionHandler expressionHandler = new DefaultMethodSecurityExpressionHandler();
try {
expressionHandler.setPermissionEvaluator(new AclPermissionEvaluator(aclService()));
} catch (PropertyVetoException e) {
throw new RuntimeException("Error creating expressionHandler", e);
}
return expressionHandler;
}
@Bean
public MutableAclService aclService() throws PropertyVetoException{
final JdbcMutableAclService mutableAclService = new JdbcMutableAclService(dataSource(), lookupStrategy(), aclCache());
mutableAclService.setClassIdentityQuery("select currval(pg_get_serial_sequence('acl_class', 'id'))");
mutableAclService.setSidIdentityQuery("select currval(pg_get_serial_sequence('acl_sid', 'id'))");
return mutableAclService;
}
public DataSource dataSource() throws PropertyVetoException {
final ComboPooledDataSource cpds = new ComboPooledDataSource(true);
cpds.setJdbcUrl(jdbcUrl);
cpds.setUser(username);
cpds.setPassword(password);
cpds.setDriverClass(driverClass);
return cpds;
}
@Bean
public BasicLookupStrategy lookupStrategy() throws PropertyVetoException {
return new BasicLookupStrategy(dataSource(), aclCache(), aclAuthorizationStrategy(), grantingStrategy(), this.defaultConversionService);
}
@Bean
public AclCache aclCache() {
return new EhCacheBasedAclCache(ehCacheManagerFactoryBean().getObject().getCache("aclCache"), grantingStrategy(), aclAuthorizationStrategy());
}
@Bean
public EhCacheManagerFactoryBean ehCacheManagerFactoryBean() {
EhCacheManagerFactoryBean cacheManagerFactoryBean = new EhCacheManagerFactoryBean();
cacheManagerFactoryBean.setConfigLocation(new ClassPathResource("ehcache.xml"));
cacheManagerFactoryBean.setShared(true);
return cacheManagerFactoryBean;
}
@Bean
public AclAuthorizationStrategy aclAuthorizationStrategy() {
return new AclAuthorizationStrategyImpl(
new SimpleGrantedAuthority("ROLE_ADMIN"),
new SimpleGrantedAuthority("ROLE_ADMIN"),
new SimpleGrantedAuthority("ROLE_ADMIN"));
}
@Bean
public PermissionGrantingStrategy grantingStrategy(){
return new DefaultPermissionGrantingStrategy(auditLogger());
}
@Bean
public AuditLogger auditLogger(){
return new ConsoleAuditLogger();
}
}
контроллер
@RepositoryRestController
@ResponseBody
public class SampleRepositoryController {
@Autowired
private MyRepository myRepository;
@PreAuthorize("hasRole('ROLE_USER') and hasPermission(#id, 'com.mycompany.mypackage.services.MyPojo', 'read')")
@RequestMapping(value = "/path/{id}/resource", method = RequestMethod.GET)
public MyObject file(@PathVariable("id") UUID id) {
return myRepository.findOne(id);
}
Когда я выполняю отладку / пошаговое выполнение DefaultMethodSecurityExpressionHandler во время выполнения, я могу сказать, что оценщиком разрешений всегда является DenyAllPermissionEvaluator (а не AclPermissionEvaluator), так как создается новый MethodSecurityEvaluationContext.
Обратите внимание, что у меня нет ни необходимости @EnableWebSecurity.
Кроме того, я сильно подозреваю, что это потому, что у меня есть @EnableGlobalMethodSecurity(prePostEnabled = true, securedEnabled = true
).
определяется дважды, но когда я снимаю его с Application.java и запускаю приложение (mvn spring-boot:run), оно не запускается со следующей ошибкой:
2016-02-22 16:04:48.747 INFO 26533 --- [lication.main()] o.apache.catalina.core.StandardService : Stopping service Tomcat
2016-02-22 16:04:48.763 WARN 26533 --- [lication.main()] o.s.boot.SpringApplication : Error handling failed (Error creating bean with name 'delegatingApplicationListener' defined in class path resource [org/springframework/security/config/annotation/web/configuration/WebSecurityConfiguration.class]: BeanPostProcessor before instantiation of bean failed; nested exception is org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'aclSecurityConfig': Injection of autowired dependencies failed; nested exception is org.springframework.beans.factory.BeanCreationException: Could not autowire method: public void org.springframework.security.config.annotation.method.configuration.GlobalMethodSecurityConfiguration.setApplicationContext(org.springframework.context.ApplicationContext); nested exception is org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'org.springframework.cache.config.internalCacheAdvisor' defined in class path resource [org/springframework/cache/annotation/ProxyCachingConfiguration.class]: Bean instantiation via factory method failed; nested exception is org.springframework.beans.BeanInstantiationException: Failed to instantiate [org.springframework.cache.interceptor.BeanFactoryCacheOperationSourceAdvisor]: Factory method 'cacheAdvisor' threw exception; nested exception is java.lang.NullPointerException)
2016-02-22 16:04:48.764 ERROR 26533 --- [lication.main()] o.s.boot.SpringApplication : Application startup failed
org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'defaultServletHandlerMapping' defined in class path resource [org/springframework/boot/autoconfigure/web/WebMvcAutoConfiguration$EnableWebMvcConfiguration.class]: Bean instantiation via factory method failed; nested exception is org.springframework.beans.BeanInstantiationException: Failed to instantiate [org.springframework.web.servlet.HandlerMapping]: Factory method 'defaultServletHandlerMapping' threw exception; nested exception is java.lang.IllegalArgumentException: A ServletContext is required to configure default servlet handling
at org.springframework.beans.factory.support.ConstructorResolver.instantiateUsingFactoryMethod(ConstructorResolver.java:599)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.instantiateUsingFactoryMethod(AbstractAutowireCapableBeanFactory.java:1119)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBeanInstance(AbstractAutowireCapableBeanFactory.java:1014)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:504)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:476)
at org.springframework.beans.factory.support.AbstractBeanFactory$1.getObject(AbstractBeanFactory.java:303)
at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:230)
at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:299)
at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:194)
at org.springframework.beans.factory.support.DefaultListableBeanFactory.preInstantiateSingletons(DefaultListableBeanFactory.java:755)
at org.springframework.context.support.AbstractApplicationContext.finishBeanFactoryInitialization(AbstractApplicationContext.java:757)
at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:480)
at org.springframework.boot.context.embedded.EmbeddedWebApplicationContext.refresh(EmbeddedWebApplicationContext.java:118)
at org.springframework.boot.SpringApplication.refresh(SpringApplication.java:686)
at org.springframework.boot.SpringApplication.run(SpringApplication.java:320)
at org.springframework.boot.SpringApplication.run(SpringApplication.java:957)
at org.springframework.boot.SpringApplication.run(SpringApplication.java:946)
at com.advaita.cloud.services.samples.Application.main(Application.java:20)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:497)
at org.springframework.boot.maven.RunMojo$LaunchRunner.run(RunMojo.java:418)
at java.lang.Thread.run(Thread.java:745)
Caused by: org.springframework.beans.BeanInstantiationException: Failed to instantiate [org.springframework.web.servlet.HandlerMapping]: Factory method 'defaultServletHandlerMapping' threw exception; nested exception is java.lang.IllegalArgumentException: A ServletContext is required to configure default servlet handling
at org.springframework.beans.factory.support.SimpleInstantiationStrategy.instantiate(SimpleInstantiationStrategy.java:189)
at org.springframework.beans.factory.support.ConstructorResolver.instantiateUsingFactoryMethod(ConstructorResolver.java:588)
... 23 common frames omitted
Caused by: java.lang.IllegalArgumentException: A ServletContext is required to configure default servlet handling
at org.springframework.util.Assert.notNull(Assert.java:112)
Кто-нибудь знает, как заставить 2 хорошо играть вместе?