Announcement Announcement Module
Collapse
No announcement yet.
No matching bean when using custom PermissionEvaluator and Spring Data JPA Page Title Module
Move Remove Collapse
X
Conversation Detail Module
Collapse
  • Filter
  • Time
  • Show
Clear All
new posts

  • No matching bean when using custom PermissionEvaluator and Spring Data JPA

    Hi

    I have a problem creating a custom PermissionEvaluator which uses a spring data jpa repository for verifying domain object access. I have attached the smallest possible sample application, showing the error. (run mvn jetty:run)

    The problem is, that when loading the app, no bean matching the repository can be found:
    Code:
    org.springframework.beans.factory.NoSuchBeanDefinitionException: No matching bean of type
    If I comment out the usage of the custom permission evaluator reference in the
    Code:
    <security:global-method-security pre-post-annotations="enabled">
       <!-- <security:expression-handler ref="expressionHandler"/> -->
      </security:global-method-security>
    And therefore are using the default denyall permission evaluator, the application loads fine.

    Can anyone give me a hint to get this working ?

    Thanks
    Christoffer

    Partlail stacktrace:
    ERROR [org.springframework.web.servlet.DispatcherServlet] - <Context initialization failed>
    org.springframework.beans.factory.BeanCreationExce ption: Error creating bean with name 'org.springframework.data.repository.core.support. RepositoryInterfaceAwareBeanPostProcessor#0': Initialization of bean failed; nested exception is org.springframework.beans.factory.BeanCreationExce ption: Error creating bean with name 'org.springframework.transaction.config.internalTr ansactionAdvisor': Cannot resolve reference to bean 'org.springframework.transaction.annotation.Annota tionTransactionAttributeSource#0' while setting bean property 'transactionAttributeSource'; nested exception is org.springframework.beans.factory.BeanCreationExce ption: Error creating bean with name 'org.springframework.transaction.annotation.Annota tionTransactionAttributeSource#0': Initialization of bean failed; nested exception is org.springframework.beans.factory.BeanCreationExce ption: Error creating bean with name 'org.springframework.security.methodSecurityMetada taSourceAdvisor': Cannot resolve reference to bean 'org.springframework.security.access.method.Delega tingMethodSecurityMetadataSource#0' while setting constructor argument; nested exception is org.springframework.beans.factory.BeanCreationExce ption: Error creating bean with name 'org.springframework.security.access.method.Delega tingMethodSecurityMetadataSource#0': Cannot create inner bean '(inner bean)' of type [org.springframework.security.access.prepost.PrePos tAnnotationSecurityMetadataSource] while setting bean property 'methodSecurityMetadataSources' with key [0]; nested exception is org.springframework.beans.factory.BeanCreationExce ption: Error creating bean with name '(inner bean)': Cannot create inner bean '(inner bean)' of type [org.springframework.security.access.expression.met hod.ExpressionBasedAnnotationAttributeFactory] while setting constructor argument; nested exception is org.springframework.beans.factory.BeanCreationExce ption: Error creating bean with name '(inner bean)': Cannot resolve reference to bean 'expressionHandler' while setting constructor argument; nested exception is org.springframework.beans.factory.BeanCreationExce ption: Error creating bean with name 'expressionHandler' defined in ServletContext resource [/WEB-INF/classes/spring_conf/appContext.xml]: Cannot resolve reference to bean 'simplePermissionEvaluator' while setting bean property 'permissionEvaluator'; nested exception is org.springframework.beans.factory.UnsatisfiedDepen dencyException: Error creating bean with name 'simplePermissionEvaluator' defined in ServletContext resource [/WEB-INF/classes/spring_conf/appContext.xml]: Unsatisfied dependency expressed through constructor argument with index 0 of type [dk.stoffer.simplemvc.service.PersonService]: : Error creating bean with name 'personService': Injection of autowired dependencies failed; nested exception is org.springframework.beans.factory.BeanCreationExce ption: Could not autowire field: private dk.stoffer.simplemvc.repositories.PersonRepository dk.stoffer.simplemvc.service.PersonService.personR epository; nested exception is org.springframework.beans.factory.NoSuchBeanDefini tionException: No matching bean of type [dk.stoffer.simplemvc.repositories.PersonRepository] found for dependency: expected at least 1 bean which qualifies as autowire candidate for this dependency. Dependency annotations: {@org.springframework.beans.factory.annotation.Aut owired(required=true)}; nested exception is org.springframework.beans.factory.BeanCreationExce ption: Error creating bean with name 'personService': Injection of autowired dependencies failed; nested exception is org.springframework.beans.factory.BeanCreationExce ption: Could not autowire field: private dk.stoffer.simplemvc.repositories.PersonRepository dk.stoffer.simplemvc.service.PersonService.personR epository; nested exception is org.springframework.beans.factory.NoSuchBeanDefini tionException: No matching bean of type [dk.stoffer.simplemvc.repositories.PersonRepository] found for dependency: expected at least 1 bean which qualifies as autowire candidate for this dependency. Dependency annotations: {@org.springframework.beans.factory.annotation.Aut owired(required=true)}

  • #2
    Apparently the initializing of the PermissionEvaluator does not work with the jpa:repositories setup. If I change the application xml to be specific about what the repository is, and manually inject this all the way up to my custom PermissionEvaluator things work like a charm.

    Is this a feature or a bug ?
    Working config snippet:
    Code:
    <!-- JPA -->
      <jpa:repositories base-package="dk.stoffer.simplemvc.repositories" >
        <jpa:repository id="personRepository"/>
      </jpa:repositories>
    	
      <security:global-method-security pre-post-annotations="enabled">
        <security:expression-handler ref="expressionHandler"/>
      </security:global-method-security>
    
      <!-- custom permission handler for domain object security using expression annotations -->
       <bean id="expressionHandler"
        class="org.springframework.security.access.expression.method.DefaultMethodSecurityExpressionHandler">
        <property name="permissionEvaluator" ref="simplePermissionEvaluator" />
      </bean>
    
      <bean id="simplePermissionEvaluator" class="dk.stoffer.simplemvc.security.SimplePermissionEvaluator" >
        <constructor-arg index="0" ref="personService" />
      </bean>
    
      <bean id="personService" class="dk.stoffer.simplemvc.service.PersonService" >
        <constructor-arg index="0" ref="personRepository" />  
      </bean>
    Non working config snippet
    Code:
     
    <!-- JPA -->
      <jpa:repositories base-package="dk.stoffer.simplemvc.repositories" />
    	
      <bean id="expressionHandler"
        class="org.springframework.security.access.expression.method.DefaultMethodSecurityExpressionHandler">
        <property name="permissionEvaluator" ref="simplePermissionEvaluator" />
      </bean>
    
      <bean id="simplePermissionEvaluator" class="dk.stoffer.simplemvc.security.SimplePermissionEvaluator" />
    where the PersonService is using annotations to get the PersonRepository injected

    Comment


    • #3
      I have hit this problem too - thanks for the workaround.
      I am currently working around this by injecting the repository with

      Code:
      @Resource(name="personRepository")
      PersonRepository personRepository;
      This doesn't seem to require modification of repository definition, i.e. it works with
      Code:
      <jpa:repositories base-package="com.example.repositories" />
      (because PersonRepository bean will be named by default as "personRepository")

      @Autowired will not work.


      Has anyone found a solution that doesn't need this workaround?

      Thanks
      Last edited by knesek1; Feb 26th, 2013, 06:19 AM. Reason: Had a typo - annotation needs be @Resource(name="personRepository") instead of @Resource("personRepository")

      Comment


      • #4
        I have this exact same issue. However the jpa xmlns seems to have changed as jpa:repository is not valid and also @Resource("repositoryName") does not compile.

        Any ideas how this can be solved?

        Comment


        • #5
          Originally posted by beginner_ View Post
          I have this exact same issue. However the jpa xmlns seems to have changed as jpa:repository is not valid and also @Resource("repositoryName") does not compile.

          Any ideas how this can be solved?
          Hmm, @Resource annotation is an annotation from javax.annotation package that seems to be a part of JDK. Are you
          sure you have imported the right annotation? ( e.g. import javax.annotation.Resource; )

          As for jpa namespace, here's how my working application context xmlns definitions look like (notice the jpa, others, like encryption, cache, etc. will not be relevant to you):
          Code:
          <?xml version="1.0" encoding="UTF-8"?>
          <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:p="http://www.springframework.org/schema/p" xmlns:aop="http://www.springframework.org/schema/aop"
          	xmlns:context="http://www.springframework.org/schema/context" xmlns:jee="http://www.springframework.org/schema/jee" xmlns:tx="http://www.springframework.org/schema/tx" xmlns:task="http://www.springframework.org/schema/task"
          	xmlns:util="http://www.springframework.org/schema/util"
          	xmlns:jpa="http://www.springframework.org/schema/data/jpa"
          	xmlns:cache="http://www.springframework.org/schema/cache"
          	xmlns:encryption="http://www.jasypt.org/schema/encryption"
          	xsi:schemaLocation="
          			http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-3.1.xsd
          			http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.1.xsd
          			http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.1.xsd
          			http://www.springframework.org/schema/jee http://www.springframework.org/schema/jee/spring-jee-3.1.xsd
          			http://www.springframework.org/schema/task http://www.springframework.org/schema/task/spring-task-3.1.xsd
          			http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-3.1.xsd
          			http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util-3.1.xsd
          			http://www.springframework.org/schema/data/jpa http://www.springframework.org/schema/data/jpa/spring-jpa.xsd
          			http://www.springframework.org/schema/cache http://www.springframework.org/schema/cache/spring-cache.xsd
          			http://www.jasypt.org/schema/encryption http://www.jasypt.org/schema/encryption/jasypt-spring31-encryption-1.xsd">

          Comment


          • #6
            Originally posted by knesek1 View Post
            Hmm, @Resource annotation is an annotation from javax.annotation package that seems to be a part of JDK. Are you
            sure you have imported the right annotation? ( e.g. import javax.annotation.Resource; )
            I'm sure it's right and looking at the documentation @Resource dos not have a value property hence @Resource("repositoryName") is invalid at least in java ee 5 or higher. And @Resource(name="repositoryName") does not work either.

            However my issue is slightly different my Resource is not in the PermissionEvaluator itself but in a class used by it.

            Comment


            • #7
              Originally posted by beginner_ View Post
              I'm sure it's right and looking at the documentation @Resource dos not have a value property hence @Resource("repositoryName") is invalid at least in java ee 5 or higher. And @Resource(name="repositoryName") does not work either.

              However my issue is slightly different my Resource is not in the PermissionEvaluator itself but in a class used by it.
              You are right, I had a typo in original post (I edited it now to reflect correct configuration). I'm injecting repository using @Resource(name="repositoryName"). This workaround seems to be working for me. Can you verify you got the repository bean name entered correctly? Check the bean names available in your application context (e.g. applicationContext.getBeanDefinitionNames() ).

              If all else fails, you can attempt a (very ugly) hack of injecting application context and fetching the repository from there.
              Something along the lines of:
              Code:
              ...
              @Autowired
              private ApplicationContext ctx;
              
              private PersonRepository personRepository;
              ...
              
              @PostConstruct
              public void init() {
                 personRespository = (PersonRepositry) ctx.getBean("personRepository");
              }
              "personRepository" should be valid bean name if your repository interface is called PersonRepository. You can
              try to specify some other name by annotating the repository interface with @Repository("someOtherName") and
              try using that to workaround the autowiring issue...

              Good luck!

              Comment


              • #8
                My solution is a simple proxy which implements PermissionEvaluator and ApplicationContextAware. It has a string property proxiedBeanName for the name of the custom permission evaluator bean. Like this, the custom permission evaluator can be built like any other regular bean, including autowired dependencies. The link to the permission system whose referenced beans seem not to undergo the same bean life cycle is made deferred at the first call to the proxied bean by calling the getDelegate() method.

                Code:
                package my.foo.bar;
                import java.io.Serializable;
                
                import org.springframework.beans.BeansException;
                import org.springframework.beans.factory.annotation.Required;
                import org.springframework.context.ApplicationContext;
                import org.springframework.context.ApplicationContextAware;
                import org.springframework.security.access.PermissionEvaluator;
                import org.springframework.security.access.expression.SecurityExpressionHandler;
                import org.springframework.security.core.Authentication;
                
                /**
                 * Loose coupling between the Spring {@link SecurityExpressionHandler} and an associated {@link PermissionEvaluator}.
                 * Reason: a permission evaluator does not undergo the same life cycle as the other beans and autowiring repositories is not
                 * possible in dependent beans without using this proxy.
                 * It delays reference of a custom {@link PermissionEvaluator} to the first execution, so the custom bean can be build independently by spring.
                 * @author till
                 *
                 */
                public class PermissionEvaluatorProxy implements PermissionEvaluator, ApplicationContextAware {
                
                    private ApplicationContext applicationContext;
                    private String proxiedBeanName;
                    private PermissionEvaluator delegate = null;
                
                    @Override
                    public boolean hasPermission(Authentication authentication, Object targetDomainObject, Object permission) {
                        return this.getDelegate().hasPermission(authentication,targetDomainObject, permission);
                    }
                
                    @Override
                    public boolean hasPermission(Authentication authentication, Serializable targetId, String targetType, Object permission) {
                        return this.getDelegate().hasPermission(authentication, targetId, targetType, permission);
                    }
                
                    @Override
                    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
                        this.applicationContext = applicationContext;
                    }
                
                    private synchronized PermissionEvaluator getDelegate() {
                        if (this.delegate == null) {
                            // use the application context to load the bean explicitly by its name
                            this.delegate = (PermissionEvaluator)this.applicationContext.getBean(this.proxiedBeanName);
                        }
                        return this.delegate;
                    }
                    /**
                     * @param delegateName the name of the referenced/proxied bean.
                     */
                    @Required
                    public void setProxiedBeanName(String delegateBeanName) {
                        this.proxiedBeanName = delegateBeanName;
                    }
                
                }
                in the spring context:

                Code:
                        <security:global-method-security pre-post-annotations="enabled" >
                           <security:expression-handler ref="methodSecurityExpressionHandler" />
                        </security:global-method-security>
                        <bean id="methodSecurityExpressionHandler" class="org.springframework.security.access.expression.method.DefaultMethodSecurityExpressionHandler">
                           <property name="permissionEvaluator">
                              <bean class="my.foo.bar.PermissionEvaluatorProxy">
                                 <property name="proxiedBeanName" value="myCustomPermissionEvaluator"/>
                              </bean>
                           </property>
                         </bean>
                
                         <bean id="myCustomPermissionEvaluator" class="my.foo.bar.MyClassImplementingPermissionEvaluator"/>
                good luck.
                Last edited by till; Mar 19th, 2013, 12:14 PM.

                Comment


                • #9
                  For those searching the forums this was reported and resolved in https://jira.springsource.org/browse/SEC-2136

                  Comment

                  Working...
                  X