我有一个Spring bean(dao)对象,该对象通过以下xml在ServletContext中实例化:
<bean id="userDao" class="com.company.dao.impl.UserDaoImpl"> <property name="sessionFactory" ref="sessionFactory" /> </bean>
该bean在我的webapp-servlet.xml文件中声明,并由我的应用程序在ServletContext中使用。
我也在使用SpringSecurity。据我了解,这在不同的上下文(SecurityContext)中运行。
我的应用程序具有webapp-security.xml,在其中实例化了自定义身份验证提供程序。我想使用在我的应用程序中使用的dao也可以在我的安全上下文中进行用户查找,但是当我运行时:
<bean id="userAuthenticationProvider" class="com.company.security.UserAuthenticationProvider"> <property name="userDao" ref="userDao" /> </bean>
我收到错误消息,说没有这样的bean“ userDao”。在我的其他上下文中声明的bean中,该bean自动装配良好,但在我的安全上下文中则没有。根据Spring Docs的说法,我认为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>
所以我的问题是,我如何访问位于SecurityContext内部的ServletContext中的DAO?我的dao是否具有作用域修饰符,还是可以在身份验证提供程序中以某种方式在运行时获取ServletContext?供参考,这是我想在身份验证提供程序中使用它的方式:
public class UserAuthenticationProvider extends AbstractUserDetailsAuthenticationProvider { @Override protected UserDetails retrieveUser(String userName, UsernamePasswordAuthenticationToken authenticationToken) throws AuthenticationException { // use dao here
感谢你向我解释
更新:
继续我的调查,似乎我在使用dao的DispatcherServlet是一个子上下文,而安全上下文位于更高的位置。因此,父上下文无法看到我的DispatcherServlet中的bean。我认为答案是将我的bean声明以某种方式移动到父应用程序上下文中,但是我不确定如何执行此操作。这是我的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> ...
我将所有dao创建的内容都移到了spring-dao.xml中,并且在spring-security.xml中,我正在执行以下操作:
<import resource="spring-dao.xml" />
daos stil对DispatcherServlet上下文仍然可见,而对我的SecurityContext不可见。
因此,问题在于我们需要确保dao存在于ApplicationContext(较高的Spring容器)中。为确保发生这种情况,我将web.xml更改为:
web.xml
<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.
在配置上下文参数之后,我立即创建了ContextLoaderListener。这是一个比DispatcherServlet更高级的spring容器,因此我认为将其放在第一位是第一个读取这些配置文件的人,然后他将创建bean。这样,任何子上下文都可以访问它们。这可能不是它的工作方式,因为DispatcherServlet甚至可能不会读取contextConfigLocation,但是即使这样做,我也认为此时已经声明了bean,所以太糟糕了,父上下文拥有它们。
现在,还有另一个窍门…为了获得我的DAO,我无法 @Autowired它。我不得不通过XML手动注入它:
<bean id="userAuthenticationProvider" class="com.company.app.security.UserAuthenticationProvider"> <property name="userDao" ref="userDao" /> </bean>
当然,我在dao上做了getter和setter方法,瞧!我不知道为什么@Autowired在这里不起作用。我认为这是设计使然。也许这是SecurityContext特有的(它不会从其他上下文中拉出),或者@Autowired通常仅从当前上下文中拉出,或者也许是因为我是通过XML创建的bean,所以我还必须通过xml和不是通过注释?(注释已启用,并且可以在我的顶级应用程序名称空间中使用)。
无论如何..我仍然不了解很多,但是重要的是它终于可以工作了。
如果要使用Spring MVC,则肯定需要了解Spring MVC的ApplicationContext层次结构。你还应该了解servlet容器中的基本组件和生命周期,因为你似乎也对侦听器和servlet的工作方式感到困惑。
简要说明你的情况:
bean <context:component-scan/>
<context:annotation-config/>