【问题标题】:'A ServletContext is required to configure default servlet handling' error when adding spring security @EnableGlobalMethodSecurity in spring boot在 Spring Boot 中添加 Spring Security @EnableGlobalMethodSecurity 时出现“配置默认 servlet 处理需要 ServletContext”错误
【发布时间】:2016-02-23 07:14:07
【问题描述】:

我正在将 Spring MVC servlet 3.1 应用程序移至 Spring Boot 1.3.0,当我将 @EnableGlobalMethodSecurity 注释添加到我的 Java 配置类之一时,我在启动时遇到异常(完整如下)。

我将 WebSecurityConfigurerAdapter 子类化为我的 Spring 安全支持(以前对我来说效果很好)

@Configuration
@EnableWebSecurity
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class SecurityAuthorisationConfig extends WebSecurityConfigurerAdapter {

@Override
protected void configure(final HttpSecurity http) throws Exception {
    http.csrf()...

即使在类中没有其他依赖项@Autowired,启动也会失败。如果我删除 @EnableGlobalMethodSecurity 注释,启动将成功,但显然没有方法安全性。大多数其他网络安全 SO 帖子似乎是由于安全配置无法在调度程序 servlet 之前启动,但我的安全配置中没有其他依赖项,我不明白为什么会发生这种情况。

我尝试了很多方法来启动应用程序并更改了安全配置的@Order,但无济于事。

我当前的应用程序入口点看起来像这样,虽然我也尝试过使用自动魔法调度程序进行初始化,但我遇到了同样的问题:

@Configuration
@EnableAutoConfiguration
@ComponentScan(basePackages = { "com.myapp.config.web", "com.myapp.config.app" })
public class Application extends SpringBootServletInitializer {
        private static Class<Application> applicationClass = Application.class;

        public static void main(final String[] args) throws Exception {
            SpringApplication.run(applicationClass, args);
        }

        @Autowired
        private AuditLogger auditLogger;

        @Override
        public void onStartup(final ServletContext container) throws ServletException {
            final AnnotationConfigWebApplicationContext rootContext = createRootContext(container);

            setUpSessionConfig(container);

            setUpMdcLoggingFilter(container);

            setAllUndefinedRequestsToUtf8(container);

            setUpAuditLogging(container);

            addSecurityFilter(container);

            addUpdateExpiredPasswordFilter(container);

            createDispatcher(container, rootContext);
        }

        private void setUpSessionConfig(final ServletContext container) {
            container.getSessionCookieConfig().setHttpOnly(true);
            container.setSessionTrackingModes(asSet(SessionTrackingMode.COOKIE));
        }

        private void setUpMdcLoggingFilter(final ServletContext container) {
            final Dynamic mdcFilter = container.addFilter("mdcInsertingFilter",
                    new DelegatingFilterProxy(mdcInsertingFilter()));
            mdcFilter.addMappingForUrlPatterns(null, true, "/*");
        }

        private void addUpdateExpiredPasswordFilter(final ServletContext container) {
            final Dynamic filter = container.addFilter("updateExpiredPasswordFilter",
                    new DelegatingFilterProxy(updateExpiredPasswordFilter()));
            filter.addMappingForUrlPatterns(null, true, "/*");
        }

        private void setUpAuditLogging(final ServletContext container) {
            final Dynamic auditLoggingFilter = container.addFilter("auditLogFilter",
                    new DelegatingFilterProxy(auditLogFilter()));
            auditLoggingFilter.addMappingForUrlPatterns(null, true, "/*");

        }

        private void addSecurityFilter(final ServletContext container) {
            final Dynamic securityFilter =
                    container.addFilter(AbstractSecurityWebApplicationInitializer.DEFAULT_FILTER_NAME,
                            DelegatingFilterProxy.class);
            securityFilter.addMappingForUrlPatterns(null, true, "/*");
        }

        private AnnotationConfigWebApplicationContext createRootContext(final ServletContext servletContext) {
            final AnnotationConfigWebApplicationContext rootContext = new AnnotationConfigWebApplicationContext();
            rootContext.register(AppConfig.class);
            rootContext.setServletContext(servletContext);

            servletContext.addListener(new ContextLoaderListener(rootContext));
            servletContext.setInitParameter("spring.profiles.default", "production");
            return rootContext;
        }

        private void setAllUndefinedRequestsToUtf8(final ServletContext container) {
            final FilterRegistration filter = container.addFilter("encodingFilter", OrderedCharacterEncodingFilter.class);
            filter.setInitParameter("encoding", "UTF-8");
            filter.addMappingForUrlPatterns(null, true, "/*");
        }

        private void createDispatcher(final ServletContext container,
                final AnnotationConfigWebApplicationContext rootContext) {
            final DispatcherServlet dispatcherServlet = new DispatcherServlet(rootContext);
            final ServletRegistration.Dynamic dispatcher =
                    container.addServlet("myServlet", dispatcherServlet);

            dispatcher.setLoadOnStartup(1);
            dispatcher.addMapping("/");
        }

        @Bean
        public AuditLogFilter auditLogFilter() {
            final AuditLogFilter auditLogFilter = new AuditLogFilter();
            auditLogFilter.setAuditLogger(auditLogger);
            auditLogFilter.setIgnoredPaths("/resources,/webjars");
            return auditLogFilter;
        }

        @Bean
        public UpdateExpiredPasswordFilter updateExpiredPasswordFilter() {
            final UpdateExpiredPasswordFilter filter = new UpdateExpiredPasswordFilter();
            filter.setPasswordUpdateFormPath("/user/change_password");
            filter.setPasswordUpdatePath("user/change_password");
            filter.setLogoutPath("/logout");
            filter.setIgnoredPaths("/resources,/webjars");
            return filter;
        }

        @Bean(name = "mdcInsertingFilter")
        public MDCInsertingServletFilter mdcInsertingFilter() {
            return new MDCInsertingServletFilter();
        }
}

虽然我不一定期望得到一个全面的解决方案,但知道从哪里开始寻找问题的原因会非常有用。它是一个错误吗?可能与我创建应用程序的方式有关吗?

堆栈跟踪:

2015-11-20 17:18:02.337 错误 80806 --- [main] os.boot.SpringApplication:应用程序启动失败 org.springframework.beans.factory.BeanCreationException:在类路径资源 [org/springframework/boot/autoconfigure/web/WebMvcAutoConfiguration$EnableWebMvcConfiguration.class] 中定义名称为“defaultServletHandlerMapping”的 bean 创建错误:通过工厂方法进行 Bean 实例化失败;嵌套异常是 org.springframework.beans.BeanInstantiationException:无法实例化 [org.springframework.web.servlet.HandlerMapping]:工厂方法“defaultServletHandlerMapping”抛出异常;嵌套异常是 java.lang.IllegalArgumentException:需要 ServletContext 来配置默认 servlet 处理 在 org.springframework.beans.factory.support.ConstructorResolver.instantiateUsingFactoryMethod(ConstructorResolver.java:599) ~[spring-beans-4.2.3.RELEASE.jar:4.2.3.RELEASE] 在 org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.instantiateUsingFactoryMethod(AbstractAutowireCapableBeanFactory.java:1123) ~[spring-beans-4.2.3.RELEASE.jar:4.2.3.RELEASE] 在 org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBeanInstance(AbstractAutowireCapableBeanFactory.java:1018) ~[spring-beans-4.2.3.RELEASE.jar:4.2.3.RELEASE] 在 org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:510) ~[spring-beans-4.2.3.RELEASE.jar:4.2.3.RELEASE] 在 org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:482) ~[spring-beans-4.2.3.RELEASE.jar:4.2.3.RELEASE] 在 org.springframework.beans.factory.support.AbstractBeanFactory$1.getObject(AbstractBeanFactory.java:306) ~[spring-beans-4.2.3.RELEASE.jar:4.2.3.RELEASE] 在 org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:230) ~[spring-beans-4.2.3.RELEASE.jar:4.2.3.RELEASE] 在 org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:302) ~[spring-beans-4.2.3.RELEASE.jar:4.2.3.RELEASE] 在 org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:197) ~[spring-beans-4.2.3.RELEASE.jar:4.2.3.RELEASE] 在 org.springframework.beans.factory.support.DefaultListableBeanFactory.preInstantiateSingletons(DefaultListableBeanFactory.java:772) ~[spring-beans-4.2.3.RELEASE.jar:4.2.3.RELEASE] 在 org.springframework.context.support.AbstractApplicationContext.finishBeanFactoryInitialization(AbstractApplicationContext.java:838) ~[spring-context-4.2.3.RELEASE.jar:4.2.3.RELEASE] 在 org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:537) ~[spring-context-4.2.3.RELEASE.jar:4.2.3.RELEASE] 在 org.springframework.boot.context.embedded.EmbeddedWebApplicationContext.refresh(EmbeddedWebApplicationContext.java:118) ~[spring-boot-1.3.0.RELEASE.jar:1.3.0.RELEASE] 在 org.springframework.boot.SpringApplication.refresh(SpringApplication.java:752) [spring-boot-1.3.0.RELEASE.jar:1.3.0.RELEASE] 在 org.springframework.boot.SpringApplication.doRun(SpringApplication.java:347) [spring-boot-1.3.0.RELEASE.jar:1.3.0.RELEASE] 在 org.springframework.boot.SpringApplication.run(SpringApplication.java:295) [spring-boot-1.3.0.RELEASE.jar:1.3.0.RELEASE] 在 org.springframework.boot.SpringApplication.run(SpringApplication.java:1112) [spring-boot-1.3.0.RELEASE.jar:1.3.0.RELEASE] 在 org.springframework.boot.SpringApplication.run(SpringApplication.java:1101) [spring-boot-1.3.0.RELEASE.jar:1.3.0.RELEASE] 在 com.amberhill.web.Application.main(Application.java:51) [bin/:na] 原因:org.springframework.beans.BeanInstantiationException:无法实例化[org.springframework.web.servlet.HandlerMapping]:工厂方法'defaultServletHandlerMapping'抛出异常;嵌套异常是 java.lang.IllegalArgumentException:需要 ServletContext 来配置默认 servlet 处理 在 org.springframework.beans.factory.support.SimpleInstantiationStrategy.instantiate(SimpleInstantiationStrategy.java:189) ~[spring-beans-4.2.3.RELEASE.jar:4.2.3.RELEASE] 在 org.springframework.beans.factory.support.ConstructorResolver.instantiateUsingFactoryMethod(ConstructorResolver.java:588) ~[spring-beans-4.2.3.RELEASE.jar:4.2.3.RELEASE] ...省略了18个常用框架 原因:java.lang.IllegalArgumentException:需要 ServletContext 来配置默认 servlet 处理 在 org.springframework.util.Assert.notNull(Assert.java:115) ~[spring-core-4.2.3.RELEASE.jar:4.2.3.RELEASE] 在 org.springframework.web.servlet.config.annotation.DefaultServletHandlerConfigurer.(DefaultServletHandlerConfigurer.java:53) ~[spring-webmvc-4.2.3.RELEASE.jar:4.2.3.RELEASE] 在 org.springframework.web.servlet.config.annotation.WebMvcConfigurationSupport.defaultServletHandlerMapping(WebMvcConfigurationSupport.java:450) ~[spring-webmvc-4.2.3.RELEASE.jar:4.2.3.RELEASE] 在 org.springframework.boot.autoconfigure.web.WebMvcAutoConfiguration$EnableWebMvcConfiguration$$EnhancerBySpringCGLIB$$5204f0e6.CGLIB$defaultServletHandlerMapping$34() ~[spring-boot-autoconfigure-1.3.0.RELEASE.jar:1.3.0.RELEASE] 在 org.springframework.boot.autoconfigure.web.WebMvcAutoConfiguration$EnableWebMvcConfiguration$$EnhancerBySpringCGLIB$$5204f0e6$$FastClassBySpringCGLIB$$7ec661f1.invoke() ~[spring-boot-autoconfigure-1.3.0.RELEASE.jar:1.3.0.发布] 在 org.springframework.cglib.proxy.MethodProxy.invokeSuper(MethodProxy.java:228) ~[spring-core-4.2.3.RELEASE.jar:4.2.3.RELEASE] 在 org.springframework.context.annotation.ConfigurationClassEnhancer$BeanMethodInterceptor.intercept(ConfigurationClassEnhancer.java:318) ~[spring-context-4.2.3.RELEASE.jar:4.2.3.RELEASE] 在 org.springframework.boot.autoconfigure.web.WebMvcAutoConfiguration$EnableWebMvcConfiguration$$EnhancerBySpringCGLIB$$5204f0e6.defaultServletHandlerMapping() ~[spring-boot-autoconfigure-1.3.0.RELEASE.jar:1.3.0.RELEASE] 在 sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) ~[na:1.8.0_11] 在 sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) ~[na:1.8.0_11] 在 sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) ~[na:1.8.0_11] 在 java.lang.reflect.Method.invoke(Method.java:483) ~[na:1.8.0_11] 在 org.springframework.beans.factory.support.SimpleInstantiationStrategy.instantiate(SimpleInstantiationStrategy.java:162) ~[spring-beans-4.2.3.RELEASE.jar:4.2.3.RELEASE] ...省略了19个常用框架

【问题讨论】:

    标签: spring-security spring-boot


    【解决方案1】:

    我终于设法通过将 @EnableGlobalMethodSecurity(prePostEnabled = true) 注释从 WebSecurityConfigurerAdapter 实现移动到它自己的配置类 并扩展 GlobalMethodSecurityConfiguration 来配置身份验证管理器。

    我觉得这是一个临时解决方案,但至少它是固定的。

    @Configuration
    @EnableGlobalMethodSecurity(prePostEnabled = true)
    public class MethodSecurityConfig extends GlobalMethodSecurityConfiguration {
        @Override
        public void configure(final AuthenticationManagerBuilder auth) throws Exception {
            auth.authenticationProvider(authenticationProvider());
        }
    
        @Bean
        public DaoAuthenticationProvider authenticationProvider() {
            final DaoAuthenticationProvider authProvider = new DaoAuthenticationProvider();
            authProvider.setPasswordEncoder(new StandardPasswordEncoder());
            authProvider.setUserDetailsService(userDetailsService());
            return authProvider;
        }
    
        @Bean
        public MyUserDetailsService userDetailsService() {
            return new MyUserDetailsService(...);
        }
    
    }
    

    【讨论】:

    • 我几乎遇到了同样的问题。我试图使用 spring-security-acl 并将其缩小到我正在创建的 AclPermissionEvaluator bean。当它被跳过时,我没有收到错误,当它出现时,我收到java.lang.IllegalArgumentException: A ServletContext is required to configure default servlet handling 错误。这是一个配置类加载顺序问题。你的回答帮我解决了。非常感谢。
    猜你喜欢
    • 2016-07-28
    • 2014-11-10
    • 2014-07-31
    • 2017-06-02
    • 2014-02-26
    • 2017-02-10
    • 2014-05-25
    • 2022-06-17
    • 2016-06-06
    相关资源
    最近更新 更多