Hi
This question is related to the inner workings of Spring. Just to be clear, I read the documentation but there probably is a detail that I missed, so I really would appreciate your help.
I have a MVC app to which I want to add Spring Security. These are my current configuration files
web.xml
The root spring container loads the servlet-context-security.xml fileCode:<web-app version="2.5" xmlns="http://java.sun.com/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"> <context-param> <param-name>contextConfigLocation</param-name> <param-value>classpath:servlet-context-security.xml</param-value> </context-param> <listener> <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class> </listener> <servlet> <servlet-name>appServlet</servlet-name> <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class> <init-param> <param-name>contextConfigLocation</param-name> <param-value>classpath:servlet-context.xml</param-value> </init-param> <load-on-startup>1</load-on-startup> </servlet> <servlet-mapping> <servlet-name>appServlet</servlet-name> <url-pattern>/</url-pattern> </servlet-mapping> <welcome-file-list> <welcome-file></welcome-file> </welcome-file-list> <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> </web-app>
The appServlet loads the servlet-context.xml fileCode:<?xml version="1.0" encoding="UTF-8"?> <beans:beans xmlns="http://www.springframework.org/schema/security" 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.xsd http://www.springframework.org/schema/security http://www.springframework.org/schema/security/spring-security.xsd"> <global-method-security pre-post-annotations="enabled"> </global-method-security> <http auto-config='true' use-expressions="true"> <intercept-url pattern="/login.jsp*" access="IS_AUTHENTICATED_ANONYMOUSLY" /> <intercept-url pattern="/**" access="ROLE_USER" /> <form-login login-page="/login.jsp" /> </http> <beans:bean id="daoAuthenticationProvider" class="org.springframework.security.authentication.dao.DaoAuthenticationProvider"> <beans:property name="userDetailsService" ref="userDetailsService"/> </beans:bean> <beans:bean id="authenticationManager" class="org.springframework.security.authentication.ProviderManager"> <beans:property name="providers"> <beans:list> <beans:ref local="daoAuthenticationProvider" /> </beans:list> </beans:property> </beans:bean> <authentication-manager> <authentication-provider user-service-ref="userDetailsService"> </authentication-provider> </authentication-manager> </beans:beans>
The servlet-context.xml loads both controllers.xml and infrastructure.xml, which are rather simple.Code:<?xml version="1.0" encoding="UTF-8"?> <beans:beans xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:jpa="http://www.springframework.org/schema/data/jpa" xmlns:beans="http://www.springframework.org/schema/beans" xmlns:mvc="http://www.springframework.org/schema/mvc" xsi:schemaLocation="http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc.xsd http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/data/jpa http://www.springframework.org/schema/data/jpa/spring-jpa.xsd"> <mvc:annotation-driven /> <mvc:resources mapping="/resources/**" location="/resources/" /> <beans:bean class="org.springframework.web.servlet.view.InternalResourceViewResolver"> <beans:property name="prefix" value="/WEB-INF/views/" /> <beans:property name="suffix" value=".jsp" /> </beans:bean> <mvc:interceptors> <beans:bean id="localeChangeInterceptor" class="org.springframework.web.servlet.i18n.LocaleChangeInterceptor"> <beans:property name="paramName" value="lang" /> </beans:bean> </mvc:interceptors> <beans:bean id="messageSource" class="org.springframework.context.support.ReloadableResourceBundleMessageSource"> <beans:property name="basename" value="classpath:messages" /> </beans:bean> <beans:bean id="localeResolver" class="org.springframework.web.servlet.i18n.CookieLocaleResolver"> <beans:property name="defaultLocale" value="en" /> </beans:bean> <beans:bean id="validator" class="org.springframework.validation.beanvalidation.LocalValidatorFactoryBean"> <beans:property name="validationMessageSource" ref="messageSource" /> </beans:bean> <beans:import resource="classpath:controllers.xml" /> <beans:import resource="classpath:infrastructure.xml" /> <jpa:repositories base-package="com.<myproject>.model.repositories" /> </beans:beans>
controllers.xml
infrastructure.xmlCode:<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:mvc="http://www.springframework.org/schema/mvc" xmlns:context="http://www.springframework.org/schema/context" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.0.xsd http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc-3.0.xsd"> <mvc:view-controller path="/" view-name="home"/> <context:component-scan base-package="com.<myproject>" /> </beans>
Ok, now you know my setup. This all works except for one part, Spring Security cannot find the "userDetailsService", although it exists like this:Code:<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:jdbc="http://www.springframework.org/schema/jdbc" xsi:schemaLocation="http://www.springframework.org/schema/jdbc http://www.springframework.org/schema/jdbc/spring-jdbc-3.0.xsd http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd"> <bean id="entityManagerFactory" class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean"> <property name="dataSource" ref="dataSource" /> <property name="persistenceUnitName" value="mysql-jpa" /> <property name="jpaVendorAdapter"> <bean class="org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter"> <property name="generateDdl" value="true" /> </bean> </property> </bean> <bean id="transactionManager" class="org.springframework.orm.jpa.JpaTransactionManager"> <property name="entityManagerFactory" ref="entityManagerFactory" /> </bean> <bean id="dataSource" class="com.mysql.jdbc.jdbc2.optional.MysqlConnectionPoolDataSource"> <property name="url" value="jdbc:mysql://localhost:3306/mydb" /> <property name="user" value="<username>" /> <property name="password" value="<password>" /> </bean> </beans>
In the servlet-context-security.xml file you'll see two beans called daoAuthenticationProvider and authenticationManager, which honestly I do not know what they do, I took them from an example.Code:@Service("userDetailsService") public class UserDetailsServiceImpl implements UserDetailsService { private Logger log = LoggerFactory.getLogger(UserDetailsServiceImpl.class); @Autowired CustomerRepository customerRep; @Override @Transactional(readOnly = true) public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException { log.debug("Getting user for username " + username); Customer customer = customerRep.findByUsername(username); if(customer == null){ throw new UsernameNotFoundException("Username " + username + " not found."); } return customer; } }
I tried something like this on that file
This worked, Spring Security used my user details service (my logs proved it) but that service was unable to autowire CustomerRepository, it was null.Code:<beans:bean id="userDetailsService" class="org.<myproject>.services.UserDetailsServiceImpl" />
From my reading and experience, I can see that servlet-context-security.xml is loaded right on the root context. servlet-context.xml, which contains the component-scan entry, is only loaded by the servlet. This makes me believe that when security is loaded my services weren't scanned yet. Problem is, I do not know how to circumvent this. I tried placing component-scan on secury.xml but got the error "No matching bean of type [com.<myproject>.model.repositories.CustomerReposit ory] found for dependency...".
Any tips or explanation or documentation link (which I'm certain i missed) would be much appreciated


Reply With Quote