I have a spring bean (dao) object which I instantiate in my ServletContext via the following xml:
<bean id="userDao" class="com.company.dao.impl.UserDaoImpl"> <property name="sessionFactory" ref="sessionFactory" /> </bean>
This bean is declared inside my webapp-servlet.xml file and is used by my app within the ServletContext.
I am also using SpringSecurity. It is my understanding that this runs in a different context (the SecurityContext).
My application has a webapp-security.xml where I instantiate a custom authentication provider. I would like to use my dao that is used in my app to also do the user lookup in my security context, but when I run:
<bean id="userAuthenticationProvider" class="com.company.security.UserAuthenticationProvider"> <property name="userDao" ref="userDao" /> </bean>
I get errors saying that there is no such bean "userDao". The bean is autowired fine in beans declared in my other context, but not inside my security context. According to the Spring Docs, I believe both separate contexts are needed in web.xml
<listener> <listener-class>org.springframework.web.context.ContextLoaderListener </listener-class> </listener> <listener> <listener-class> org.springframework.security.web.session.HttpSessionEventPublisher </listener-class> </listener>
So my question is, how can I get access to my DAO that lives in my ServletContext inside my SecurityContext? Is there a scope modifier for my dao, or could I somehow get the ServletContext at runtime within my authentication provider? For reference, this is how I want to use it inside my authentication provider:
public class UserAuthenticationProvider extends AbstractUserDetailsAuthenticationProvider { @Override protected UserDetails retrieveUser(String userName, UsernamePasswordAuthenticationToken authenticationToken) throws AuthenticationException { // use dao here
thanks for explaining this to me
UPDATE:
Continuing my investigation, it seems that the DispatcherServlet where I'm using my daos is a child context, and the security context is somewhere higher up. Consequently, beans in my DispatcherServlet can not be seen by parent contexts. I think the answer is to move my bean declarations into the parent application context somehow, but i'm not sure how to do this. Here is my web.xml
<context-param> <param-name>contextConfigLocation</param-name> <param-value>WEB-INF/spring-*.xml </param-value> </context-param> <listener> <listener-class> org.springframework.security.web.session.HttpSessionEventPublisher </listener-class> </listener> <listener> <listener-class>org.springframework.web.context.ContextLoaderListener </listener-class> </listener> <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> <servlet> <servlet-name>myapp</servlet-name> <servlet-class>org.springframework.web.servlet.DispatcherServlet </servlet-class> <load-on-startup>1</load-on-startup> <init-param> <param-name>listings</param-name> <param-value>true</param-value> </init-param> </servlet> ...
I moved all of my dao creation out to a spring-dao.xml, and in my spring-security.xml I am now doing a:
<import resource="spring-dao.xml" />
The daos stil remain visible to the DispatcherServlet context and invisible to my SecurityContext though.
ANSWERED:
Alright, I figured it out. Here were some helpful links:
http://static.springsource.org/spring/docs/3.0.x/spring-framework-reference/html/beans.html#context-create
http://forum.springsource.org/showthread.php?115774-Spring-Security-Custom-UserDetailsService-to-use-User-Service-Dao
http://static.springsource.org/spring-security/site/faq.html#faq-method-security-in-web-context
So the problem was we need to make sure that the dao exists in the ApplicationContext (higher up spring container). To make sure this happened I changed my web.xml to be:
<context-param> <param-name>contextConfigLocation</param-name> <param-value>WEB-INF/spring-dao.xml WEB-INF/spring-security.xml </param-value> </context-param> <listener> <listener-class>org.springframework.web.context.ContextLoaderListener </listener-class> </listener> <listener> <listener-class> org.springframework.security.web.session.HttpSessionEventPublisher </listener-class> </listener> <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> <servlet> <servlet-name>webapp</servlet-name> <servlet-class>org.springframework.web.servlet.DispatcherServlet </servlet-class> <load-on-startup>1</load-on-startup> <init-param> <param-name>listings</param-name> <param-value>true</param-value> </init-param> </servlet>
I thought this would make sure that the first context loader that starts up will read my dao config (and create my dao beans), then my security config. Since the dao beans are being created this way, I removed the previous "import resource="spring-dao.xml"" statement in the security.xml because it will no longer be needed.
Right after that context-param configuration I created the ContextLoaderListener. This is a higher-level spring container than the DispatcherServlet, so I figured putting this first would be the first guy to read those config files, and he would then create the beans. Then, any child-context would have access to them. This may not be how it works as DispatcherServlet may not even read the contextConfigLocation, but even if it does, I figured that at this point the beans would already be declared, so too bad, the parent context owns them.
Now, for another trick... in order to get my DAO, I could not @Autowired it. I had to manually inject it via XML:
<bean id="userAuthenticationProvider" class="com.company.app.security.UserAuthenticationProvider"> <property name="userDao" ref="userDao" /> </bean>
Of course, I made the getter and setter methods on my dao, and voila! I do not know why the @Autowired does not work here. I assume it's by design. Perhaps this is particular to the SecurityContext (it will not pull from other contexts), or perhaps @Autowired in general only pulls from the current context, or maybe because I created the bean via XML, I have to also set any properties via xml and not via annotations? (annotations are enabled and working in my top-level application namespace).
Anyways.. still a lot I don't understand, but the important point is it's finally working.
Beans from parent context are visible in child context but not vice-versa. That way, a child context can use beans and configuration from parent context and override only what's necessary. Example of that can be a security defined in parent context and used/overridden in child contexts.
In Spring, the objects that form the backbone of your application and that are managed by the Spring IoC container are called beans. A bean is an object that is instantiated, assembled, and otherwise managed by a Spring IoC container.
“parent context” is the name we use to name the context that would be inherited by the child (where child adds it own job to).
Annotating a class with the @Configuration indicates that the class can be used by the Spring IoC container as a source of bean definitions. The @Bean annotation tells Spring that a method annotated with @Bean will return an object that should be registered as a bean in the Spring application context.
If you're going to use Spring MVC, you definitely need to understand Spring MVC's ApplicationContext hierarchy. You should also learn something about the basic components and lifecycles in a servlet container, since you seem to be confused about how listeners and servlets work, too.
To explain your situation briefly:
<context:component-scan/>
or <context:annotation-config/>
in the context where that bean exists.If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With