#security #spring #ioc-container #servlets #security-context
#Безопасность #spring #ioc-контейнер #сервлеты #безопасность-контекст
Вопрос:
У меня есть объект spring bean (dao), который я создаю в своем ServletContext с помощью следующего xml:
<bean id="userDao" class="com.company.dao.impl.UserDaoImpl">
<property name="sessionFactory" ref="sessionFactory" />
</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» не существует. Компонент автоматически подключается в beans, объявленных в моем другом контексте, но не в моем контексте безопасности. Согласно документам 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 внутри моего SecurityContext? Есть ли модификатор области видимости для моего dao, или я могу каким-то образом получить ServletContext во время выполнения в моем поставщике аутентификации? Для справки, вот как я хочу использовать его внутри моего поставщика аутентификации:
public class UserAuthenticationProvider extends
AbstractUserDetailsAuthenticationProvider {
@Override
protected UserDetails retrieveUser(String userName,
UsernamePasswordAuthenticationToken authenticationToken)
throws AuthenticationException {
// use dao here
спасибо, что объяснили мне это
Обновить:
Продолжая мое расследование, кажется, что DispatcherServlet, в котором я использую свои dao, является дочерним контекстом, а контекст безопасности находится где-то выше. Следовательно, компоненты в моем DispatcherServlet не могут быть видны родительскими контекстами. Я думаю, что ответ заключается в том, чтобы каким-то образом переместить мои объявления компонентов в контекст родительского приложения, но я не уверен, как это сделать. Вот мой 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>
Я думал, что это гарантирует, что первый запускаемый загрузчик контекста прочитает мою конфигурацию dao (и создаст мои компоненты dao), а затем мою конфигурацию безопасности. Поскольку компоненты dao создаются таким образом, я удалил предыдущий «import resource =»spring-dao.xml «» заявление в security.xml потому что в нем больше не будет необходимости.
Сразу после этой настройки параметров контекста я создал ContextLoaderListener. Это контейнер spring более высокого уровня, чем DispatcherServlet, поэтому я решил, что если поставить это первым, то первым будет парень, который прочитает эти конфигурационные файлы, а затем создаст компоненты. Тогда любой дочерний контекст будет иметь к ним доступ. Возможно, это работает не так, поскольку DispatcherServlet может даже не прочитать contextConfigLocation , но даже если это произойдет, я полагал, что на данный момент компоненты уже будут объявлены, так что, к сожалению, они принадлежат родительскому контексту.
Теперь, для другого трюка … чтобы получить мой DAO, я не смог @Autowired его. Мне пришлось вручную вводить его через XML:
<bean id="userAuthenticationProvider" class="com.company.app.security.UserAuthenticationProvider">
<property name="userDao" ref="userDao" />
</bean>
Конечно, я создал методы getter и setter в своем dao, и вуаля! Я не знаю, почему @Autowired здесь не работает. Я предполагаю, что это сделано специально. Возможно, это относится к SecurityContext (он не будет извлекаться из других контекстов), или, возможно, @Autowired вообще извлекается только из текущего контекста, или, может быть, потому, что я создал компонент через XML, я должен также устанавливать любые свойства через xml, а не через аннотации? (аннотации включены и работают в моем пространстве имен приложений верхнего уровня).
В любом случае .. я все еще многого не понимаю, но важным моментом является то, что он наконец работает.
Ответ №1:
Если вы собираетесь использовать Spring MVC, вам обязательно нужно понимать иерархию ApplicationContext Spring MVC. Вам также следует кое-что узнать об основных компонентах и жизненных циклах в контейнере сервлетов, поскольку вы, похоже, тоже не понимаете, как работают слушатели и сервлеты.
Чтобы кратко объяснить вашу ситуацию:
- Вы создаете два ApplicationContexts: корневой контекст и контекст DispatcherServlet. Корневой контекст создается ContextLoaderListener на основе файлов, названных в contextConfigLocation . Этот контекст предназначен для содержания компонентов, составляющих основную логику вашего приложения. Контекст DispatcherServlet создается при запуске этого сервлета и основан на файле с именем «webapp-servlet.xml «. Этот контекст предназначен для содержания любых компонентов, которые поддерживают экземпляр DispatcherServlet, с которым он связан, и в нем должны быть только компоненты, связанные с представлением.
- Контекст DispatcherServlet становится дочерним элементом корневого контекста. Это позволяет вводить ваши основные компоненты из корневого контекста в компоненты уровня представления. Видимость односторонняя. Компоненты уровня представления недоступны для основных компонентов, что желательно. Вот почему ваш DAO не удалось внедрить в вашего поставщика аутентификации. DAO был в дочернем контексте.
- Службы на основе аннотаций применяются только в контексте, в котором они объявлены. Если @Autowired не работает для определенного компонента, это потому, что вы не объявили
<context:component-scan/>
или<context:annotation-config/>
в контексте, где этот компонент существует.
Комментарии:
1. это интересно, что вы не можете @autowired в разных контекстах, но вы можете вводить xml в разных контекстах. спасибо за информацию
2. Нет, это неверно. Извините, если я был неясен. @Autowired отлично работает в разных контекстах. Однако «компонент обработки», который выполняет @Autowiring, существует внутри и работает только с одним контекстом. Таким образом, для заданных компонентов
a
иb
в контекстахA
иB
, соответственно, гдеa
есть@Autowired b
поле, это поле будет установлено автоматически, только если 1)A
содержит «компонент обработки» и 2)A
является дочерним элементомB
. Этот «компонент обработки» создается тегами, которые я связал в части 3 моего ответа. Наличие этого компонента обработкиB
не повлияет на компонентыA
.3. как насчет applicationContext-security.xml в моем заявлении? создает ли он компоненты в корневом контексте или контексте сервлета?
4. @anton1980 — это зависит от того, где
applicationContext-security.xml
загружается файл. Импортируется ли он в определение DispatcherServlet или вContextLoaderListener
5. @RyanStewart, можно ли использовать
@ComponentScan
его в обоих контекстах? Когда я попытался использовать его в корневом контексте, каким-то образом все мои компоненты были созданы во второй раз и в веб-контексте, и в итоге я получил повторяющиеся вызовы некоторых моих служб.