Spring Boot с AclPermissionEvaluator, приводящий к IllegalStateException: не задан ServletContext
Привет эксперты,
В настоящее время я изучаю Spring Boot и хочу использовать его с Spring Security ACL. После документации Spring Security и учебного пособия на Baeldung.com, я думаю, что я понял, что нужно. Я также посмотрел на пример DMS Spring. Я наткнулся на другой пример в поисках решения.
На основании этой информации я создал приложение. Для справки, вы можете найти текущее приложение на GitHub.
Текущий выпуск
Когда я запускаю приложение, я получаю java.lang.IllegalStateException: No ServletContext set
выброшены. Насколько я понимаю, это связано с тем, что во время магии автоконфигурации Spring Boot мой @Configuration
аннотированные классы инициализируются до ServletContext
инициализируется.
Ссылка на полный стек трассировки на pastebin.
Что я сделал до сих пор
Основываясь на моих исследованиях (в основном на Stackru) и моем текущем понимании проблемы, это должно помочь Bean
с в собственный @Configuration
аннотированный класс. К сожалению, здесь я сейчас потерялся. Связанные вопросы привели меня к этому размышлению (см. Этот вопрос или этот.)
Моя среда
- macOS 10.12.6
- jdk1.8.0_144
- IntelliJ IDEA 2017.3.3 (Ultimate Edition)
- Spring Boot v1.5.9.RELEASE
Соответствующие файлы проекта
pom.xml
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0
http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>de.moritzrupp</groupId>
<artifactId>traderdiary</artifactId>
<version>0.0.1-SNAPSHOT</version>
<packaging>jar</packaging>
<name>Trader Diary</name>
<description>Trader Diary is an easy to use web application to create a journal
about your trades with great reporting on top of it.</description>
<licenses>
<license>
<name>GNU General Public License (GPL) v3.0</name>
<url>https://www.gnu.org/licenses/gpl-3.0.txt</url>
</license>
</licenses>
<developers>
<developer>
<id>moritzrupp</id>
<name>Moritz Rupp</name>
<email>moritz.rupp@gmail.com</email>
<url>https://www.moritzrupp.de</url>
<timezone>DE</timezone>
</developer>
</developers>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>1.5.9.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<java.version>1.8</java.version>
</properties>
<dependencies>
<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.data</groupId>
<artifactId>spring-data-rest-hal-browser</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-hateoas</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-acl</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-config</artifactId>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context-support</artifactId>
</dependency>
<dependency>
<groupId>net.sf.ehcache</groupId>
<artifactId>ehcache-core</artifactId>
<version>2.6.11</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
<scope>runtime</scope>
<optional>true</optional>
</dependency>
<dependency>
<groupId>com.h2database</groupId>
<artifactId>h2</artifactId>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework.restdocs</groupId>
<artifactId>spring-restdocs-mockmvc</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
application.properties
# --------------------------------------------
# Datasource Properties
# --------------------------------------------
spring.h2.console.enabled=true
spring.h2.console.path=/h2
spring.datasource.url=jdbc:h2:mem:trader-diary-h2-db
spring.datasource.platform=h2
spring.datasource.username=sa
spring.datasource.password=
spring.datasource.driver-class-name=org.h2.Driver
spring.jpa.hibernate.ddl-auto=validate
spring.jpa.properties.hibernate.dialect=org.hibernate.dialect.H2Dialect
TraderDiaryApplication.java
@SpringBootApplication
public class TraderDiaryApplication {
public static void main(String[] args) {
SpringApplication.run(TraderDiaryApplication.class, args);
}
}
DataSourceConfig.java
@Configuration
public class DataSourceConfig {
@Bean
public DataSource traderDiaryDataSource(DataSourceProperties
dataSourceProperties) {
return dataSourceProperties.initializeDataSourceBuilder().build();
}
}
WebSecurityConfig.java
@Configuration
@EnableGlobalMethodSecurity(prePostEnabled = true, securedEnabled = true)
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
@Autowired
@Qualifier("traderDiaryDataSource")
private DataSource dataSource;
@Override
protected void configure(HttpSecurity http) throws Exception {
// I think it is not relevant for the issue, see GitHub repo
...
}
@Bean
public PasswordEncoder passwordEncoder() {
// I think it is not relevant for the issue, see GitHub repo
...
}
@Bean
public DaoAuthenticationProvider daoAuthenticationProvider(...) {
// I think it is not relevant for the issue, see GitHub repo
...
}
@Bean
public MethodSecurityExpressionHandler aclExpressionHandler() {
DefaultMethodSecurityExpressionHandler expressionHandler =
new DefaultMethodSecurityExpressionHandler();
AclPermissionCacheOptimizer permissionCacheOptimizer =
new AclPermissionCacheOptimizer(aclService());
expressionHandler.setPermissionEvaluator(permissionEvaluator());
expressionHandler
.setPermissionCacheOptimizer(permissionCacheOptimizer);
return expressionHandler;
}
@Bean
public PermissionEvaluator permissionEvaluator() {
return new AclPermissionEvaluator(aclService());
}
@Bean
public JdbcMutableAclService aclService() {
return new JdbcMutableAclService(dataSource, lookupStrategy(),
aclCache());
}
@Bean
public LookupStrategy lookupStrategy() {
return new BasicLookupStrategy(dataSource, aclCache(),
aclAuthorizationStrategy(), new ConsoleAuditLogger());
}
@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 PermissionGrantingStrategy permissionGrantingStrategy() {
return new DefaultPermissionGrantingStrategy(new
ConsoleAuditLogger());
}
@Bean
public AclAuthorizationStrategy aclAuthorizationStrategy() {
return new AclAuthorizationStrategyImpl(
new SimpleGrantedAuthority("ROLE_ADMIN"));
}
/* This is due to an earlier issue: DataSource required */
@Configuration
protected static class AclMethodSecurityConfig extends
GlobalMethodSecurityConfiguration {
@Autowired
@Qualifier("daoAuthenticationProvider")
private AuthenticationProvider authenticationProvider;
@Autowired
@Qualifier("aclExpressionHandler")
private MethodSecurityExpressionHandler aclExpressionHandler;
@Autowired
public void configureAuthManager(AuthenticationManagerBuilder
authenticationManagerBuilder) {
authenticationManagerBuilder
.authenticationProvider(authenticationProvider);
}
@Override
protected MethodSecurityExpressionHandler
createExpressionHandler() {
return aclExpressionHandler;
}
}
}
Большое спасибо за весь вклад! Если потребуется дополнительная информация, я с радостью ее предоставлю.
Спасибо и наилучшие пожелания Мориц
2 ответа
Всем привет,
Мне удалось решить проблему самостоятельно. Я сделал пошаговый подход, комментируя / раскомментируя все MethodSecurity
связанные вещи.
Я указал это на создание DefaultMethodSecurityExpressionHandler
, Это было причиной IllegalStateException: No ServletContext set
,
Затем я создал новый класс MethodSecurityConfig.java
и я положил весь связанный код там. Теперь приложение запускается снова, и я могу продолжить с разработанным.
@ Rakesh: Спасибо за ваш вклад!
MethodSecurityConfig.java
@Configuration
public class MethodSecurityConfig {
private DataSource dataSource;
@Autowired
@Qualifier("traderDiaryDataSource")
public void setDataSource(DataSource dataSource) {
this.dataSource = dataSource;
}
@Bean
public MethodSecurityExpressionHandler aclExpressionHandler() {
DefaultMethodSecurityExpressionHandler expressionHandler =
new DefaultMethodSecurityExpressionHandler();
AclPermissionCacheOptimizer permissionCacheOptimizer =
new AclPermissionCacheOptimizer(aclService());
expressionHandler.setPermissionEvaluator(permissionEvaluator());
expressionHandler
.setPermissionCacheOptimizer(permissionCacheOptimizer);
return expressionHandler;
}
@Bean
public PermissionEvaluator permissionEvaluator() {
return new AclPermissionEvaluator(aclService());
}
@Bean
public JdbcMutableAclService aclService() {
return new JdbcMutableAclService(dataSource,
lookupStrategy(), aclCache());
}
@Bean
public LookupStrategy lookupStrategy() {
return new BasicLookupStrategy(dataSource, aclCache(),
aclAuthorizationStrategy(), new ConsoleAuditLogger());
}
@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 PermissionGrantingStrategy permissionGrantingStrategy() {
return new DefaultPermissionGrantingStrategy(
new ConsoleAuditLogger());
}
@Bean
public AclAuthorizationStrategy aclAuthorizationStrategy() {
return new AclAuthorizationStrategyImpl(
new SimpleGrantedAuthority("ROLE_ADMIN"));
}
}
WebSecurityConfig.java
@Configuration
@EnableGlobalMethodSecurity(securedEnabled = true)
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
...
@Configuration
protected static class AclMethodSecurityConfig
extends GlobalMethodSecurityConfiguration {
private MethodSecurityExpressionHandler aclExpressionHandler;
@Autowired
@Qualifier("aclExpressionHandler")
public void setAclExpressionHandler(
MethodSecurityExpressionHandler aclExpressionHandler) {
this.aclExpressionHandler = aclExpressionHandler;
}
@Override
protected MethodSecurityExpressionHandler createExpressionHandler() {
return aclExpressionHandler;
}
}
}
Просто чтобы проконтролировать ваш собственный ответ и мой предыдущий комментарий, в котором я тоже был потерян (более 2 дней!). В конце концов он также переопределял метод configure(AuthenticationManagerBuilder auth) в подклассе GlobalMethodSecurityConfiguration, который также был аннотирован @EnableGlobalMethodSecurity.
Поскольку я новичок в Spring, я, к сожалению, не могу предоставить подробную информацию о том, почему это так, но если я добавлю настройку AuthenticationManagerBuilder в класс, который расширяет WebSecurityConfigurereAdapter (как у меня было изначально), то я всегда получаю IllegalStateException: No ServletContext set
ошибка.
Вот последний код, который я использовал:
@Configuration
@EnableGlobalMethodSecurity( prePostEnabled = true, securedEnabled = true )
public class MethodSecurityConfig extends GlobalMethodSecurityConfiguration {
@Override
protected MethodSecurityExpressionHandler createExpressionHandler() {
return defaultMethodSecurityExpressionHandler();
}
@Override
protected void configure( final AuthenticationManagerBuilder authenticationManagerBuilder ) throws Exception {
authenticationManagerBuilder.authenticationProvider( authenticationProvider() );
}
@Bean
public JdbcDaoImpl jdbcDao() {
JdbcDaoImpl jdbcDao = new JdbcDaoImpl();
jdbcDao.setDataSource( dataSource() );
return jdbcDao;
}
@Bean
public DaoAuthenticationProvider authenticationProvider() {
final DaoAuthenticationProvider authenticationProvider = new DaoAuthenticationProvider();
authenticationProvider.setUserDetailsService( jdbcDao() );
authenticationProvider.setPasswordEncoder( encoder() );
return authenticationProvider;
}
@Bean
public PasswordEncoder encoder() {
return new BCryptPasswordEncoder( 11 );
}
@Bean( name = "myDS")
public DataSource dataSource() {
BasicDataSource dataSource = new BasicDataSource();
dataSource.setDriverClassName( "com.mysql.cj.jdbc.Driver" );
dataSource.setUrl( "jdbc:mysql://localhost:3306/java_spring_angular_rest_security_acl2" );
dataSource.setUsername( "root" );
dataSource.setPassword( "alphax" );
return 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 LookupStrategy lookupStrategy() {
return new BasicLookupStrategy(dataSource(), aclCache(), aclAuthorizationStrategy(), permissionGrantingStrategy());
}
@Bean
public AclAuthorizationStrategy aclAuthorizationStrategy() {
return new AclAuthorizationStrategyImpl(new SimpleGrantedAuthority("ROLE_ADMIN"));
}
@Bean
public PermissionGrantingStrategy permissionGrantingStrategy() {
return new DefaultPermissionGrantingStrategy(new ConsoleAuditLogger());
}
@Bean( name = "defaultMethodSecurityExpressionHandler")
public MethodSecurityExpressionHandler defaultMethodSecurityExpressionHandler() {
DefaultMethodSecurityExpressionHandler expressionHandler = new DefaultMethodSecurityExpressionHandler();
AclPermissionEvaluator permissionEvaluator = new AclPermissionEvaluator(aclService());
expressionHandler.setPermissionEvaluator(permissionEvaluator);
expressionHandler.setPermissionCacheOptimizer( new AclPermissionCacheOptimizer( aclService() ) );
return expressionHandler;
}
@Bean( name = "myAclService")
public JdbcMutableAclService aclService() {
JdbcMutableAclService jdbcMutableAclService = new JdbcMutableAclService(dataSource(), lookupStrategy(), aclCache());
jdbcMutableAclService.setClassIdentityQuery( "SELECT @@IDENTITY" );
jdbcMutableAclService.setSidIdentityQuery( "SELECT @@IDENTITY" );
return jdbcMutableAclService;
}
}
а вот и pom.xml
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.fireduptech.spring.rest</groupId>
<artifactId>hero</artifactId>
<version>0.0.1-SNAPSHOT</version>
<packaging>jar</packaging>
<name>hero</name>
<description>Demo project for Spring Boot</description>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>1.5.9.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<java.version>1.8</java.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<!-- https://mvnrepository.com/artifact/mysql/mysql-connector-java -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>6.0.6</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-rest</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-test</artifactId>
<scope>test</scope>
</dependency>
<!-- https://mvnrepository.com/artifact/org.apache.commons/commons-dbcp2 -->
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-dbcp2</artifactId>
<version>2.2.0</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.springframework.security/spring-security-acl -->
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-acl</artifactId>
<version>5.0.1.RELEASE</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.springframework/spring-context-support -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context-support</artifactId>
<version>5.0.3.RELEASE</version>
</dependency>
<!-- https://mvnrepository.com/artifact/net.sf.ehcache/ehcache-core -->
<dependency>
<groupId>net.sf.ehcache</groupId>
<artifactId>ehcache-core</artifactId>
<version>2.6.11</version>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
Надеюсь, что это помогает кому-то:)