在父上下文与子上下文中声明 Spring Bean
我有一个 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 文档,我相信 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
感谢您向我解释这一点
更新:
继续我的调查,看来我使用 daos 的 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 不可见尽管。
已回答:
好吧,我明白了。这里有一些有用的链接:
http://static.springsource。 org/spring-security/site/faq.html#faq-method-security-in-web-context
所以问题是我们需要确保 dao 存在于 ApplicationContext(更高层的 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 beans),然后读取我的安全配置。由于 dao bean 是以这种方式创建的,因此我删除了 security.xml 中先前的“import resource="spring-dao.xml"”语句,因为不再需要它。
在上下文参数配置之后,我创建了 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 而不是通过注释设置任何属性? (注释已启用并在我的顶级应用程序命名空间中工作)。
无论如何..还有很多我不明白的地方,但重要的是它终于可以工作了。
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-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.
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(1)
如果你要使用 Spring MVC,你肯定需要 了解 Spring MVC 的 ApplicationContext 层次结构。您还应该了解Servlet 容器中的基本组件和生命周期,因为您似乎也对侦听器和 servlet 的工作方式感到困惑。
简要解释一下您的情况:
或 <代码>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.