4

我正在尝试将 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 被初始化。这会导致存储库引用为空。

以下 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> 

服务 bean 配置:

<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" />

持久性 bean 配置:

<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" />

用户存储库:

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 依赖项。

编辑:添加了用户存储库代码。

4

6 回答 6

2

我遇到了这里描述的同样的问题。我注意到两个弹簧工厂。

  1. 来自dispacher-servlet.xml,它加载@Service @Repository 类,因为组件扫描定义在基本包级别,因此我可以将@Autowire Service 类放入Controller。
  2. 来自应用程序上下文的@Autowire 类似乎没有标记为@Service,因为它们没有加载。
于 2012-09-12T18:53:40.750 回答
1

我也遇到过这个问题。它与Spring容器中bean初始化的顺序有关。解决方法不是自动装配存储库,而是让您的领域实现 ApplicationContextAware 并直接从上下文中获取所需的 bean。它并不优雅,但它会起作用。

于 2013-06-20T01:41:12.440 回答
1

如果我理解正确,您应该能够创建一个子类ShiroFilterFactoryBeanimplements org.springframework.beans.factory.InitializingBean。然后,InitializingBean.afterPropertiesSet()您将添加一些获取UserRepository并将其设置到该字段的代码。不是最优雅的解决方案,但这看起来像是一个例外情况。

于 2012-05-18T18:06:28.733 回答
0

取自ShiroFilterFactoryBean-and-a-spring-data-mongodb-realm 的具体问题解释:

问题是 spring-data-mongodb 需要一个 spring ApplicationEventMulticaster 在它可以使用之前已经被初始化。

ShiroFilterFactoryBean 是一个 beanPostProcessor,因此,在初始化期间,spring 尝试配置其领域(因此我的领域和基于 mongo 的 spring 数据 userDao)。它失败是因为尚未创建 ApplicationEventMulticaster。

在我尝试了几种解决此问题的建议方法后,例如InitializingBeanApplicationContextAwareBeanPostProcessor接口(每个都会导致过早调用,因此在初始化我必要的服务/存储库之前),我想出了以下解决方案:

  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" />

至(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>

优点:

  • 有效xD
  • 没有额外的负担/依赖于你的 shiro 东西
  • 完成spring配置和设置,初始化后状态一致

坏处:

  • 一次性开销设置
  • 可能会导致不一致的状态,如果您忘记或碰撞胶水配置,它将在运行时而不是在启动时抱怨
于 2013-11-09T12:05:27.390 回答
0

我不太确定这是否有帮助,但您可以从我这里查看这个问题以获取替代解决方案。

但是,核心问题可能仍然悬而未决。

于 2012-05-28T13:40:38.847 回答
0

ShiroFilterFactoryBean 实现了 BeanPostProcessor,并且由于它依赖于安全管理器以及它自己对数据存储、数据访问对象等的依赖关系。它可能导致大量Y 类型的 Bean X 不符合被所有人处理的条件BeanPostProcessors消息。

最糟糕的是,它似乎只是一种查看 Spring 实例化的过滤器实现的方式,以便跟踪并可能将属性注入 AuthorizationFilters。

坦率地说,我不需要仅仅为过滤器跟踪而头疼,所以我创建了一个不包含 BeanPostProcessor 的自定义版本。我现在被迫将过滤器实现手动连接到 bean 的“过滤器”属性,但至少我不必处理该错误以及我的安全管理器和相关 bean 的可疑状态。

于 2018-07-18T13:30:49.997 回答