使用AclPermissionEvaluator进行Spring Boot导致IllegalStateException:没有设置ServletContext

时间:2018-01-21 14:00:49

标签: java spring spring-boot spring-security spring-security-acl

您好专家,

我目前正在学习Spring Boot,我想将它与Spring Security ACL一起使用。 在documentation的Spring Security和Baeldung.com的教程之后,我想我已经了解了所需的内容。我也调查了DMS example of Spring。我通过搜索解决方案偶然发现了另一个example

根据这些信息,我构建了我的应用程序。作为参考,您可以在GitHub上找到当前的应用程序。

当前问题

当我启动应用程序时,我收到了java.lang.IllegalStateException: No ServletContext set。据我所知,这是因为在Spring Boot的自动配置 magic 期间,我的@Configuration注释类在ServletContext之前被初始化。初始化。

链接到pastebin上的完整堆栈跟踪。

到目前为止我做了什么

根据我的研究(主要是在StackOverflow上)以及我目前对该问题的理解,它应该有助于将负责的Bean放入一个自己的@Configuration注释类中。不幸的是,我现在迷路了。 相关问题让我想到了这一点(请参阅this questionthis one。)

我的环境

  • macOS 10.12.6
  • jdk1.8.0_144
  • IntelliJ IDEA 2017.3.3(终极版)
  • 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;
        }
    }
}

非常感谢所有输入!如果需要任何进一步的信息,我很乐意提供。

谢谢和最诚挚的问候 Moritz的

2 个答案:

答案 0 :(得分: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;
        }
    }
}

答案 1 :(得分:2)

只是跟进你自己的回答和我以前的评论,我也迷失了这个(超过2天!)。最后,它还覆盖了 GlobalMethodSecurityConfiguration 的子类中的 configure(AuthenticationManagerBuilder auth) 方法,该方法也是使用 @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>

希望有人帮助:)