Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Duplicate registration for springSecurityFilterChain when deploying on tomcat outside of eclipse

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

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

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

See picture for full stack trace.

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

  • 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.

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

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

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.

Thanks!

picture of full stack trace

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

web.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

<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>
like image 855
farnett Avatar asked Sep 18 '14 01:09

farnett


2 Answers

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

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 two configurations for the springSecurityFilterChain. Take one out, and you will be making progress.

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.

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:

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.

like image 116
JBCP Avatar answered Oct 21 '22 05:10

JBCP


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

<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.

like image 28
Odwori Avatar answered Oct 21 '22 07:10

Odwori