在 Eclipse 之外的 tomcat 上部署时重复注册 springSecurityFilterChain

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/25902804/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-09-19 21:59:34  来源:igfitidea点击:

Duplicate registration for springSecurityFilterChain when deploying on tomcat outside of eclipse

javaeclipsespringtomcat

提问by farnett

I am a little new to spring and still confused by all the configurations. I went by several different tutorials and it seems like everyone does things differently. I have a spring application which runs fine through Eclipse using the tomcat plugin. However when exporting a war file to tomcat itself tomcat doesn't start and throws

我对 spring 有点陌生,仍然对所有配置感到困惑。我看了几个不同的教程,似乎每个人做的事情都不一样。我有一个 spring 应用程序,它使用 tomcat 插件通过 Eclipse 运行良好。但是,当将 war 文件导出到 tomcat 本身时,tomcat 不会启动并抛出

SEVERE: ContainerBase.addChild: start org.apache.catalina.LifecycleException: Failed to start component

严重:ContainerBase.addChild:启动 org.apache.catalina.LifecycleException:无法启动组件

Caused by: java.lang.IllegalStateException: Duplicate Filter registration for 'springSecuirtyFilterChain'. Check to insure the Filter is only configured once!

引起:java.lang.IllegalStateException:'springSecuirtyFilterChain' 的重复过滤器注册。检查以确保过滤器仅配置一次!

See picture for full stack trace.

有关完整堆栈跟踪,请参见图片。

After commenting out springSecurityFilterChain in web.xml, whether dataSource is autowired or not gives one or two errors.

在web.xml中注释掉springSecurityFilterChain后,dataSource是否自动装配都会出现一两个错误。

  • If dataSource is autowired then I just get an error saying creating bean securityConfig failed and that there is no bean found for dependency.

  • If I leave dataSource not autowired (like the code I have which works in Eclipse) then I get an IllegalArgumentException: Property 'dataSource' is required.

  • 如果 dataSource 是自动装配的,那么我只会收到一条错误消息,指出创建 bean securityConfig 失败并且没有找到依赖项的 bean。

  • 如果我让 dataSource 没有自动装配(就像我在 Eclipse 中工作的代码一样),那么我会得到一个 IllegalArgumentException: Property 'dataSource' is required。

Also in order to not get a multiple ContextLoader definition error I have to comment out the ContextLoaderListener in web xml.

此外,为了不出现多个 ContextLoader 定义错误,我必须在 web xml 中注释掉 ContextLoaderListener。

From what I've seen the problem lies in using xml and java for configurations but I can't pinpoint exactly what is wrong.

从我所看到的问题在于使用 xml 和 java 进行配置,但我无法确定到底出了什么问题。

I found a similar question but was unable to solve my problem. Where do I define `springSecurityFilterChain` bean?Adding a bean class pointing to my securityConfig put in spring-security.xml didn't help.

我发现了一个类似的问题,但无法解决我的问题。 我在哪里定义`springSecurityFilterChain` bean?在 spring-security.xml 中添加一个指向我的 securityConfig 的 bean 类没有帮助。

Thanks!

谢谢!

picture of full stack trace

完整堆栈跟踪的图片

Below is the code which works totally fine when running in Eclipse.

下面是在 Eclipse 中运行时完全正常的代码。

web.xml

网页.xml

<web-app id="WebApp_ID" version="2.4"
    xmlns="http://java.sun.com/xml/ns/j2ee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee 
    http://java.sun.com/xml/ns/j2ee/web-app_2_4.xsd">


        <!-- Spring MVC -->
    <servlet>
        <servlet-name>mvc-dispatcher</servlet-name>
        <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
        <load-on-startup>1</load-on-startup>
    </servlet>

      <servlet>
    <servlet-name>InitServlet</servlet-name>
    <servlet-class>servlet.InitServlet</servlet-class>
    <init-param>
      <param-name>configfile</param-name>
    </init-param>
    <load-on-startup>2</load-on-startup>
  </servlet>

  <servlet>
    <servlet-name>AdminServlet</servlet-name>
    <servlet-class>servlet.admin.AdminServlet</servlet-class>
    <load-on-startup>3</load-on-startup>
  </servlet>

  <servlet>
    <servlet-name>UserServlet</servlet-name>
    <servlet-class>servlet.user.UserServlet</servlet-class>
    <load-on-startup>4</load-on-startup>
  </servlet>

  <servlet>
    <servlet-name>SignupUserServlet</servlet-name>
    <servlet-class>servlet.user.SignupUserServlet</servlet-class>
    <load-on-startup>5</load-on-startup>
  </servlet>

  <servlet>
   <servlet-name>ReceiveFile</servlet-name>
    <servlet-class>servlet.user.ReceiveFile</servlet-class>
    <load-on-startup>6</load-on-startup>
  </servlet>

  <servlet-mapping>
        <servlet-name>mvc-dispatcher</servlet-name>
        <url-pattern>/pages/*</url-pattern>
    </servlet-mapping>

  <servlet-mapping>
   <servlet-name>AdminServlet</servlet-name>
   <url-pattern>/AdminServlet</url-pattern>
  </servlet-mapping>

  <servlet-mapping>
   <servlet-name>UserServlet</servlet-name>
   <url-pattern>/UserServlet</url-pattern>
  </servlet-mapping>

  <servlet-mapping>
   <servlet-name>SignupUserServlet</servlet-name>
   <url-pattern>/SignupUserServlet</url-pattern>
  </servlet-mapping>

  <servlet-mapping>
   <servlet-name>ReceiveFile</servlet-name>
   <url-pattern>/ReceiveFile</url-pattern>
  </servlet-mapping>

    <listener>
        <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
    </listener>

    <context-param>
        <param-name>contextConfigLocation</param-name>
        <param-value>
            /WEB-INF/spring-security.xml,
            /WEB-INF/spring-database.xml
        </param-value>
    </context-param>

    <!-- Spring Security -->
    <!-- This is to allow enctype="multipart/form-data" to upload and not throw an access denied page. 
    See bottom of http://docs.spring.io/spring-security/site/docs/3.2.0.CI-SNAPSHOT/reference/html/csrf.html  for more info.-->
    <filter>
    <filter-name>MultipartFilter</filter-name>
    <filter-class>org.springframework.web.multipart.support.MultipartFilter</filter-class>
  </filter>

  <filter>
        <filter-name>springSecurityFilterChain</filter-name>
        <filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>
    </filter> 

 <filter-mapping>
        <filter-name>springSecurityFilterChain</filter-name>
        <url-pattern>/*</url-pattern>
    </filter-mapping>

    <filter-mapping>
    <filter-name>MultipartFilter</filter-name>
    <url-pattern>/*</url-pattern>
    </filter-mapping>


</web-app>

SecuirtyConfig.java

安全配置文件

@Configuration
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {

  DataSource dataSource;

/*    @Autowired
    public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
    //    auth.inMemoryAuthentication().withUser("user").password("password").roles("USER");
    } */

    @Autowired
    public void configAuthentication(AuthenticationManagerBuilder auth) throws Exception {

        auth.jdbcAuthentication().dataSource(dataSource)
            .usersByUsernameQuery("select username,password, enabled from test_users where username=?")
            .authoritiesByUsernameQuery("select username, role from test_user_roles where username=?");
    }   

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.csrf().disable()
            .authorizeRequests()
              .antMatchers("/res/**").permitAll()
              .anyRequest().authenticated()
              .and()
            .formLogin()
              .loginPage("/loginDatabase.html")
              .permitAll();
    }
}

AppConfig.java

应用程序配置文件

@EnableWebMvc
@Configuration
@ComponentScan({"security.spring"})
@Import({ SecurityConfig.class })
public class AppConfig {

    @Bean(name = "dataSource")
    public DriverManagerDataSource dataSource() {
        DriverManagerDataSource driverManagerDataSource = new DriverManagerDataSource();
        driverManagerDataSource.setDriverClassName("com.mysql.jdbc.Driver");
        driverManagerDataSource.setUrl("****");
        driverManagerDataSource.setUsername("**");
        driverManagerDataSource.setPassword("**");
        return driverManagerDataSource;
    }

    @Bean
    public InternalResourceViewResolver viewResolver() {
        InternalResourceViewResolver viewResolver = new InternalResourceViewResolver();
        viewResolver.setViewClass(JstlView.class);
        viewResolver.setPrefix("/WEB-INF/pages/");
        viewResolver.setSuffix(".jsp");
        return viewResolver;
    }

}

spring-security.xml

spring-security.xml

<beans:beans xmlns="http://www.springframework.org/schema/security"
  xmlns:context="http://www.springframework.org/schema/context"  
    xmlns:beans="http://www.springframework.org/schema/beans" 
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://www.springframework.org/schema/beans
    http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
    http://www.springframework.org/schema/security
    http://www.springframework.org/schema/security/spring-security-3.2.xsd
    http://www.springframework.org/schema/context 
  http://www.springframework.org/schema/context/spring-context-3.0.xsd">

    <context:component-scan base-package="spring.*" />
    <!-- enable use-expressions -->
    <http auto-config="true" use-expressions="true">
        <!-- login page must be available to all. The order matters, if this is after something which secures the page this will fail. -->
<!--        <intercept-url pattern="/SignupUserServlet" access="permitAll"/> -->
<!--        <intercept-url pattern="/pages/ReceiveFile" access="permitAll()"/> 
        <intercept-url pattern="/pages/fileUpdate2" access="permitAll()"/>
    <intercept-url pattern="/pages/login" access="permitAll()" />  -->
        <intercept-url pattern="/pages/admin/**" access="hasRole('_admin')" />
        <intercept-url pattern="/pages/trade/**" access="hasRole('_trader')" />
        <intercept-url pattern="/pages/discover/**" access="hasRole('_users')" />       
        <!-- access denied page -->
        <access-denied-handler error-page="/pages/403" />
        <form-login 
            login-page="/pages/login" 
            default-target-url="/pages/common/redirectportal" 
            authentication-failure-url="/pages/login?error" 
            username-parameter="username"
            password-parameter="password" />
        <logout logout-url="/pages/logout" logout-success-url="/pages/login?logout" />
        <!-- enable csrf protection -->
        <!-- currently off for testing... <csrf/> -->
    </http>

    <!-- Select users and user_roles from database -->
    <authentication-manager>
        <authentication-provider ref="customAuthenticationProvider"/>
        <!--<jdbc-user-service data-source-ref="dataSource"
                users-by-username-query=
                    "select email,pwhash, enabled from users where email=?"
                authorities-by-username-query=
                    "select email, groupname from usergroups where email =?  " /> 
        </authentication-provider> -->
    </authentication-manager>

</beans:beans>

采纳答案by JBCP

It looks like you have two instances of the springSecurityFilterChaindefined: Once in SecurityConfig.java, and once in spring-security.xml. You only need one of those files.

看起来您有两个springSecurityFilterChain定义的实例:Once inSecurityConfig.java和 once in spring-security.xml。您只需要这些文件之一。

The filter line in web.xml tells the servlet engine (Tomcat) to load that filter, but the instance of that filter is configured in the Spring context. The problem is the Spring context can't start, because you have twoconfigurations for the springSecurityFilterChain. Take one out, and you will be making progress.

web.xml 中的过滤器行告诉 servlet 引擎 (Tomcat) 加载该过滤器,但该过滤器的实例是在 Spring 上下文中配置的。问题是Spring上下文无法启动,因为你有2个的配置springSecurityFilterChain。拿出一个,你就会进步。

Your configuration in the XML file seems more comprehensive and fine-grained, but I would recommend moving that configuration to the Java file and eliminating the XML file.

您在 XML 文件中的配置似乎更全面和更细粒度,但我建议将该配置移至 Java 文件并删除 XML 文件。

Once you remove your duplicate configuration, you may still have errors, but you should be able to find a solution to those on this site, or feel free to post a separate question!

删除重复的配置后,您可能仍然有错误,但您应该能够在本网站上找到解决方案,或者随时发布单独的问题!

Note: It is also possible to get Spring to automatically register the filter chain for you, so you don't need to define it in web.xml. See here for how to do that:

注意:也可以让 Spring 自动为你注册过滤器链,所以你不需要在 web.xml 中定义它。请参阅此处了解如何执行此操作:

http://www.mkyong.com/spring-security/spring-security-hello-world-annotation-example/

http://www.mkyong.com/spring-security/spring-security-hello-world-annotation-example/

However, I would recommend getting the current config working first, before throwing that in the mix.

但是,我建议先让当前配置工作,然后再将其混入。

回答by Odwori

if you have this class

如果你有这门课

import org.springframework.security.web.context.AbstractSecurityWebApplicationInitializer;

public class SpringSecurityInitializer extends AbstractSecurityWebApplicationInitializer {
   //do nothing
}

The above class is equivalent to the following web.xml code

上面的类相当于下面的web.xml代码

<filter>
    <filter-name>springSecurityFilterChain</filter-name>
    <filter-class>org.springframework.web.filter.DelegatingFilterProxy
            </filter-class>
</filter>

<filter-mapping>
    <filter-name>springSecurityFilterChain</filter-name>
    <url-pattern>/*</url-pattern>
</filter-mapping>

Just remove the one you do not want.

只需删除您不想要的那个。

回答by Mohammad Adnan

I think there is no exact answer to this. Posting it anyway may be it would help someone. Two steps for xml approach -

我认为对此没有确切的答案。无论如何发布它可能会帮助某人。xml 方法的两个步骤 -

  1. get rid of SpringSecurityInitializer class because u are using xml and have mapping in web.xml
  2. In AppConfig file remove @Import({ SecurityConfig.class })
  1. 摆脱 SpringSecurityInitializer 类,因为您正在使用 xml 并在 web.xml 中有映射
  2. 在 AppConfig 文件中删除 @Import({ SecurityConfig.class })

If you want to keep them both, then do the reverse i.e. remove mapping from web.xml file and get rid of xml.

如果您想同时保留它们,则执行相反的操作,即从 web.xml 文件中删除映射并删除 xml。