Spring MongoDB и Apache Shiro

Я пытаюсь использовать Apache Shiro с Spring и MongoDB. Я использую Spring Data Repositories, которые подключены автоматически. Я создал свою собственную область для Shiro, которая использует репозиторий Spring Data для общения с Mongo:

public class PlatformRealm extends AuthorizingRealm {

    @Autowired(required = true)
    protected UserRepository userRepository = null;

    @Override
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
         ...
    }
}

Проблема, которую я вижу, заключается в том, что userRepository не подключается автоматически. В выводе консоли я получаю следующую строку, относящуюся к PlatformRealm:

INFO  org.springframework.web.context.support.XmlWebApplicationContext  - Bean 'platformRealm' of type [class com.resonance.platform.core.security.PlatformRealm] is not eligible for getting processed by all BeanPostProcessors (for example: not eligible for auto-proxying)

Это из-за Apache Shiro ShiroFilterFactoryBean. Происходит то, что этот bean-компонент и все его зависимости загружаются сразу после запуска контейнера. Он не дожидается инициализации моих компонентов сохранения состояния до разрешения зависимостей. Это приводит к тому, что ссылка на репозиторий становится пустой.

Следующие конфигурации bean-компонентов загружаются через параметр contextConfigLocation:

<context-param> 
    <param-name>contextConfigLocation</param-name> 
    <param-value>
        /WEB-INF/web-platform-persistence.xml,
        /WEB-INF/web-platform-services.xml
    </param-value> 
</context-param> 

Конфигурация компонента служб:

<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context"
xmlns:util="http://www.springframework.org/schema/util"
xsi:schemaLocation="http://www.springframework.org/schema/context
      http://www.springframework.org/schema/context/spring-context-3.0.xsd
      http://www.springframework.org/schema/beans
      http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
      http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util-3.0.xsd">

<bean id="userSession"
    class="com.resonance.platform.web.core.services.ShiroUserSessionService" />

<!-- Shiro (Security) -->
<bean id="shiroFilter" class="org.apache.shiro.spring.web.ShiroFilterFactoryBean">
    <property name="securityManager" ref="securityManager" />
    <property name="loginUrl" value="/login" />
    <property name="successUrl" value="/" />
    <!-- The 'filters' property is not necessary since any declared javax.servlet.Filter 
        bean -->
    <!-- defined will be automatically acquired and available via its beanName 
        in chain -->
    <!-- definitions, but you can perform instance overrides or name aliases 
        here if you like: -->
    <!-- <property name="filters"> <util:map> <entry key="anAlias" value-ref="someFilter"/> 
        </util:map> </property> -->
    <property name="filterChainDefinitions">
        <value>
            # some example chain definitions:
            /admin/** = passThruFilter, roles[admin]
            /** = passThruFilter
        </value>
    </property>
</bean>

<bean id="passThruFilter"
    class="org.apache.shiro.web.filter.authc.PassThruAuthenticationFilter" />

<bean id="securityManager" class="org.apache.shiro.web.mgt.DefaultWebSecurityManager">
    <!-- Single realm app. If you have multiple realms, use the 'realms' property 
        instead. -->
    <property name="realm" ref="platformRealm" />
    <!-- By default the servlet container sessions will be used. Uncomment 
        this line to use shiro's native sessions (see the JavaDoc for more): -->
    <!-- <property name="sessionMode" value="native"/> -->
</bean>

<bean id="lifecycleBeanPostProcessor" class="org.apache.shiro.spring.LifecycleBeanPostProcessor" />

<bean class="org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator"
    depends-on="lifecycleBeanPostProcessor" />

<bean class="org.apache.shiro.spring.security.interceptor.AuthorizationAttributeSourceAdvisor">
    <property name="securityManager" ref="securityManager" />
</bean>

<!-- Define the Shiro Realm implementation you want to use to connect to 
    your back-end -->
<!-- security datasource: -->
<bean id="platformRealm" class="com.resonance.platform.core.security.PlatformRealm" />

Persistence bean config:

<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context"
xmlns:mongo="http://www.springframework.org/schema/data/mongo"
xmlns:util="http://www.springframework.org/schema/util"
xsi:schemaLocation="http://www.springframework.org/schema/context
      http://www.springframework.org/schema/context/spring-context-3.0.xsd
      http://www.springframework.org/schema/data/mongo
      http://www.springframework.org/schema/data/mongo/spring-mongo-1.0.xsd
      http://www.springframework.org/schema/beans
      http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
      http://www.springframework.org/schema/util
      http://www.springframework.org/schema/util/spring-util-3.0.xsd">

<mongo:mongo id="mongo" />

<bean id="mongoTemplate" class="org.springframework.data.mongodb.core.MongoTemplate">
    <constructor-arg ref="mongo" />
    <constructor-arg value="platform" />
    <property name="writeConcern">
        <util:constant static-field="com.mongodb.WriteConcern.SAFE" ></util:constant>
    </property>
</bean>

<mongo:repositories base-package="com.resonance.platform.core.data.repositories" />

User Repository:

package com.resonance.platform.core.data.repositories;

import org.bson.types.ObjectId;
import org.springframework.data.repository.CrudRepository;

import com.resonance.platform.core.entities.User;

/**
 * A repository used to manage User entities.
 * @author Kyle
 */
public interface UserRepository extends CrudRepository<User, ObjectId> {

    /**
     * Gets a user by the specified login.
     * @param login
     * @return
     */
    User getByLogin(String login);

}

Мой вопрос в том, как я могу правильно разрешить зависимость userRepository? Я понимаю, что ShiroFilterFactoryBean должен быть инициализирован до других зависимостей и прочего, но должен быть способ разрешить зависимость userRepository.

РЕДАКТИРОВАТЬ: добавлен код пользовательского репозитория.


person SoWeLie    schedule 08.05.2012    source источник
comment
Можете ли вы разместить код в своем репозитории пользователей?   -  person sourcedelica    schedule 09.05.2012
comment
Я полагаю, это просто интерфейс, поскольку платформа Spring Data автоматически создает реальную реализацию.   -  person SoWeLie    schedule 10.05.2012
comment
Загружаются ли файлы spring.xml как часть той же среды выполнения ApplicationContext? Или одна часть контекста приложения, а другая часть файла foo-servlet.xml?   -  person Les Hazlewood    schedule 10.05.2012
comment
Файлы конфигурации сохраняемости и службы загружаются контекстом через свойство contextConfigLocation в web.xml. Обновлю оригинал.   -  person SoWeLie    schedule 10.05.2012
comment
попробуйте что-нибудь вроде ‹bean id = shiroFilter depends-on = userRepository ...›   -  person Konstantin V. Salikhov    schedule 16.05.2012
comment
В проекте, над которым я сейчас работаю, также используются shiro и spring, а наш userDao без проблем подключается к нашему Царству. Единственное заметное отличие, которое я вижу, заключается в том, что мы используем фабричный компонент для создания нашей области. Если все остальное не помогает, вы всегда можете ввести фильтр shiro в область и использовать afterPropertiesSet () из InitializingBean, чтобы установить dao в фильтре.   -  person G-Man    schedule 16.05.2012
comment
Когда вы говорите, что используете фабричный компонент для создания своей области, вы имеете в виду настраиваемый фабричный компонент? Будут ли мои зависимости в области автоматически подключаться?   -  person SoWeLie    schedule 18.05.2012


Ответы (6)


Я столкнулся с той же проблемой, описанной здесь. Замечаю две весенние фабрики.

  1. из dispacher-servlet.xml, который загружает классы @Service @Repository из-за сканирования компонентов, определенного на уровне базового пакета, поэтому я могу использовать класс @Autowire Service в Controller.
  2. из контекста приложения не кажется, что классы @Autowire помечены как @Service, потому что они не загружены.
person Jiggy    schedule 12.09.2012

Если я правильно вас понял, вы сможете создать подкласс ShiroFilterFactoryBean, который реализует org.springframework.beans.factory.InitializingBean. Затем в InitializingBean.afterPropertiesSet() вы должны добавить код, который получает UserRepository и устанавливает его в это поле. Не самое элегантное решение, но похоже на исключительный случай.

person FelixM    schedule 18.05.2012
comment
Проблема в том, что я не совсем уверен, как инициализируется UserRepository. Прямо сейчас spring инициализирует его под крышками в модуле Spring Data. - person SoWeLie; 19.05.2012

У меня тоже была эта проблема. Это как-то связано с порядком инициализации bean-компонентов в контейнере Spring. Обходной путь заключается не в автоматическом подключении репозитория, а в том, чтобы ваша область реализовала ApplicationContextAware и получила необходимые bean-компоненты прямо из контекста. Это не изящно, но сработает.

person Andrew Weiland    schedule 20.06.2013

Я не уверен, что это полезно, но вы можете проверить этот вопрос от меня в качестве альтернативного решения.

Но основная проблема, вероятно, все еще остается открытой.

person user1323865    schedule 28.05.2012

Конкретное объяснение проблемы взято из заголовка ShiroFilterFactoryBean-and-a-spring-data-mongodb-realm:

Проблема в том, что spring-data-mongodb требует, чтобы Spring ApplicationEventMulticaster был инициализирован, прежде чем его можно будет использовать.

ShiroFilterFactoryBean - это beanPostProcessor, и поэтому во время инициализации Spring пытается настроить свои области (и, следовательно, мой userDao на основе данных realm и spring на основе mongo). это не удается, потому что ApplicationEventMulticaster еще не создан.

После того, как я попробовал несколько предлагаемых способов решения этой проблемы, таких как интерфейсы InitializingBean, ApplicationContextAware или BeanPostProcessor (каждый из которых приводит к преждевременному вызову, следовательно, до инициализации моих необходимых материалов службы / репозитория), я пришел к следующему решению:

  1. Позвольте Spring создать ваш контекст shiro без какого-либо автоматического разрешения bean-компонентов для ваших сервисов / репозиториев.
  2. Пусть Spring создаст контекст вашего сервиса / репозитория, включая mongodb
  3. Создайте простой класс, который позаботится о вашей связке shiro-service, и настройте его соответствующим образом в вашей конфигурации spring. Этот класс будет вызываться после успешной настройки контекста shiro и службы.

К (1), ул. нравится:

<bean id="shiroFilter" class="org.apache.shiro.spring.web.ShiroFilterFactoryBean">
    <property name="securityManager" ref="securityManager" />
    <property name="filterChainDefinitions">
        <value>
            <!-- Your definitions -->
        </value>
    </property>
</bean>

<bean id="securityManager" class="org.apache.shiro.web.mgt.DefaultWebSecurityManager"
        p:realm-ref="myShiroRealm" />

<bean id="myShiroRealm" class="com.acme.MyShiroRealm" 
    <!--no bean refs here-->
/>

К (2), ул. нравится:

<bean id="myService" class="com.acme.MyService"
        c:myRepository-ref="myRepository" />

...

<!-- Ask Spring Data to scan our repositories -->
<mongo:repositories base-package="com.acme.repository.impl.mongodb" />

To (3):

public class ShiroRealmServiceBridge {
    public static void postInject( MyShiroServerRealm realm, MyService service ) {
        realm.setService( service );
    }
}

<bean class="org.springframework.beans.factory.config.MethodInvokingFactoryBean">
    <property name="targetClass"><value>com.acme.ShiroRealmServiceBridge</value></property>
    <property name="targetMethod"><value>postInject</value></property>
    <property name="arguments">
    <list>
        <ref bean="myShiroRealm" />
        <ref bean="myService" />
    </list>
</property>

Advantages:

  • Работает xD
  • Никаких дополнительных нагрузок / зависимостей от ваших вещей широ
  • Полная конфигурация и настройка пружины, приводящая к согласованному состоянию после инициализации

Недостаток:

  • Одноразовая установка накладных расходов
  • Может привести к несогласованному состоянию, которое будет жаловаться во время выполнения, а не при запуске, если вы забудете или измените конфигурацию клея.
person cbopp    schedule 09.11.2013

ShiroFilterFactoryBean реализует BeanPostProcessor и, поскольку он имеет зависимости от диспетчера безопасности с собственными зависимостями от хранилищ данных, объектов доступа к данным и т. Д., Он может вызвать целый ряд Bean X типа Y не могут обрабатываться всеми сообщениями BeanPostProcessors.

Хуже всего то, что, похоже, это всего лишь способ увидеть реализации фильтра, которые Spring создает для отслеживания и, возможно, внедрения свойств в AuthorizationFilters.

Честно говоря, мне не нужна эта головная боль только для отслеживания фильтров, поэтому я создал специальную версию, которая не включала BeanPostProcessor. Теперь я вынужден вручную подключать реализации фильтра к свойству beans "filters", но, по крайней мере, мне не нужно иметь дело с этой ошибкой и сомнительным статусом моего диспетчера безопасности и связанных компонентов.

person dnebing    schedule 18.07.2018