9

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>
Community
  • 1
  • 1
farnett
  • 480
  • 2
  • 8
  • 18

3 Answers3

8

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.

JBCP
  • 13,109
  • 9
  • 73
  • 111
  • Could you elaborate on the "once in spring-security.xml" comment? I thought I was configuring the springSecurityFilterChain with the filter line in web.xml. – farnett Sep 18 '14 at 15:44
  • I had something like the SpringSecurityInitializer in your example in my application, however I wasn't using it because it was already talking about multiple defined filters. I am currently trying to change my configuration to java to see if that works... – farnett Sep 18 '14 at 23:23
  • good luck, report back what you find. As I said, you can leave your web.xml the way you have and and don't worry about SpringSecurityInitializer for now. We have one of our applications setup like this (with Java Security config, filter chain in web.xml) and it works fine. – JBCP Sep 19 '14 at 03:58
  • 1
    While working to change my project and get rid of the xml, eclipse crashed with a perm gen space error and lost all of my window/view (and I'm assuming other) preferences. Somehow this must have changed the way the war file was exported, because after that with the same code it all the sudden started working. Unfortunately, I'm not sure what happened. I will mark your answer as correct since it put me on a path that *somehow* it started working. Normally something explainable happens and my code breaks thankfully this seems to be the exception where it went the other way. – farnett Sep 19 '14 at 20:46
3

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.

Odwori
  • 1,460
  • 13
  • 14
1

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

  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 })

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

Mohammad Adnan
  • 6,527
  • 6
  • 29
  • 47
  • I got the same problem and solved it by removing just public class SecurityWebApplicationInitializer extends AbstractSecurityWebApplicationInitializer, – RK Shrestha May 15 '20 at 16:03