我有一个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>
我收到了"没有名为"userDao"的bean"的错误提示。在我的其他上下文中声明的bean可以自动装配,但在安全上下文中却不行。根据Spring文档,我相信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>
我的问题是,我如何在安全上下文中访问我的DAO,它存储在我的ServletContext中? 我的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" />
这些 DAO 对于 DispatcherServlet 上下文可见,但对于我的 SecurityContext 是不可见的。
回答:
好的,我明白了。下面是一些有用的链接:
http://static.springsource.org/spring-security/site/faq.html#faq-method-security-in-web-context
问题出在我们需要确保 DAO 存在于应用程序上下文(更高层次的 Spring 容器)中。为了确保这一点,我改变了我的 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>
我认为这样做可以确保首个上下文加载器会读取我的dao配置文件(并创建dao bean),然后再读取安全性配置文件。由于通过这种方式创建了dao bean,因此我在security.xml中删除了之前的"import resource="spring-dao.xml""语句,因为它已不再需要。
紧接着context-param配置,我创建了ContextLoaderListener。这是一个高级spring容器,比DispatcherServlet更高级,我想把它放在最前面来读取这些配置文件,然后“他”将创建这些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而不是注释设置任何属性? (注释在我的顶级应用程序命名空间中启用并正常工作)。总之,还有很多我不理解的地方,但重要的是它终于运行起来了。
a
和b
,其中a
具有一个@Autowired b
字段,那么只有在 1)A
包含一个“处理 bean”并且 2)A
是B
的子级时,该字段才会自动设置。这个“处理 bean”是由我在答案第三部分中链接的标签创建的。将该处理 bean 放在B
中对A
中的 bean 没有影响。 - Ryan StewartapplicationContext-security.xml
加载的位置。它是在 DispatcherServlet 定义中导入还是在ContextLoaderListener
中导入? - kapad@ComponentScan
可以吗?当我尝试在根上下文中使用它时,所有的bean都在Web上下文中创建了第二次,最终导致我的某些服务被重复调用。 - lanoxx@ComponentScan
包树,而不是在父级中也扫描它们。 - Mark Wood