Spring MongoDB and Apache Shiro

I am trying to use Apache Shiro with Spring and MongoDB. I use Spring Data Warehouses, which are auto-updated. I created my own custom world for Shiro, which uses the Spring datastore to talk to Mongo:

public class PlatformRealm extends AuthorizingRealm { @Autowired(required = true) protected UserRepository userRepository = null; @Override protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException { ... } } 

The problem that I see is that userRepository is not automatically supported. I get the following line in my console release, citing 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) 

This is because of Apache Shiro ShiroFilterFactoryBean. This bean happens, and all its dependencies are loaded immediately after the container starts. This does not wait until my beans save is initialized before the dependencies are resolved. This causes the repository link to be null.

The following bean configurations are loaded through the contextConfigLocation parameter:

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

Services bean Configuration:

 <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 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 of 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); } 

My question is, how can I correctly determine the dependency of userRepository? I understand that ShiroFilterFactoryBean needs to be initialized to other dependencies and something else, but there must be a way to get the userRepository dependency.

EDIT: added user repository code.

+4
source share
5 answers

I ran into the same problem described here. I notice two spring factories.

  • from dispacher-servlet.xml, which loads the @Service @Repository classes due to checking the components defined at the base package level, so I can use the @Autowire Service class in the controller.
  • from the application context, @Autowire does not seem to classes marked as @Service because they are not loaded.
+2
source

If you understand correctly, you should create a subclass of ShiroFilterFactoryBean that implements org.springframework.beans.factory.InitializingBean . In InitializingBean.afterPropertiesSet() you would add code that receives a UserRepository and sets it to this field. Not the smartest solution, but it looks like an exceptional case.

+1
source

I also had this problem. This has something to do with the bean initialization order in the Spring container. The workaround is not to auto-consolidate the repository, but that your area implements ApplicationContextAware and gets the necessary beans directly from the context. It is not elegant, but it will work.

+1
source

I'm not sure if this is useful, but you can check this question for an alternative solution.

But the main problem probably remains open.

0
source

An explanation of a specific problem, taken from ShiroFilterFactoryBean-and-a-spring-data-mongodb-realm :

The problem is that spring -data-mongodb requires springApplicationEventMulticaster must be initialized before it can be used.

ShiroFilterFactoryBean is a beanPostProcessor, and as such, during initialization, spring tries to customize its scope (and therefore my realm and spring data based on mongo userDao user). this does not work because ApplicationEventMulticaster has not yet been created.

After I tried several suggested ways to solve this problem, for example, the InitializingBean , ApplicationContextAware or BeanPostProcessor (each of which leads to a premature call, therefore, before initializing my required service / repository material), I came up with the following solution:

  • Let spring create your wide context without automatically resolving the bean for your services / repositories.
  • Let spring create your service / repository context including mongodb
  • Create a simple class that takes care of your connection with the wide service and sets it up accordingly in your spring configuration. This class will be called after successfully setting up your siro and service.

To (1), sth. eg:

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

To (2), sth. eg:

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

Benefits:

  • It works xD
  • No additional loads / dependencies on your strong material.
  • Configuring and configuring spring, resulting in a consistent state after initialization

Inconvenience:

  • One-time setup of service data
  • May lead to an inconsistent state that will complain at runtime, and not at startup, if you forget or damage the glue configuration
0
source

Source: https://habr.com/ru/post/1411353/


All Articles