Results 1 to 9 of 9

Thread: No matching bean when using custom PermissionEvaluator and Spring Data JPA

  1. #1

    Default 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)}
    Attached Files Attached Files

  2. #2

    Default

    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

  3. #3
    Join Date
    Mar 2012
    Posts
    6

    Default

    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 at 06:19 AM. Reason: Had a typo - annotation needs be @Resource(name="personRepository") instead of @Resource("personRepository")

  4. #4
    Join Date
    Oct 2012
    Posts
    6

    Default

    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?

  5. #5
    Join Date
    Mar 2012
    Posts
    6

    Default

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

  6. #6
    Join Date
    Oct 2012
    Posts
    6

    Default

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

  7. #7
    Join Date
    Mar 2012
    Posts
    6

    Default

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

  8. #8
    Join Date
    Mar 2013
    Posts
    1

    Default

    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 at 12:14 PM.

  9. #9
    Join Date
    Jan 2008
    Posts
    1,826

    Default

    For those searching the forums this was reported and resolved in https://jira.springsource.org/browse/SEC-2136
    Rob Winch - @rob_winch
    Spring Security Lead
    Pivotal

Tags for this Thread

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •