Spring Security ACL intercept-methods with transaction advice

I recently used Spring to configure a manager to use Spring Security ACL method-level authorization. Here's a snip of my initial manager bean config:
<bean id="clientManager" class="com.acme.service.impl.ClientManagerImpl">    <sec:intercept-methods access-decision-manager-ref="accessDecisionManager">        <sec:protect method="get" access="AFTER_ACL_READ"/>        <sec:protect method="save" access="ROLE_ADMIN,ACL_WRITE"/>    </sec:intercept-methods>    <constructor-arg ref="clientDao"/>    <property name="mutableAclService" ref="aclService"/></bean>
With this configuration, my unit tests verified that the method-level authorization was working as expected. Next, I applied some declarative transaction management to the same manager:
<aop:config>    <aop:advisor id="managerTx" advice-ref="txAdvice" pointcut="execution(* *..service.*Manager.*(..))" order="0"/></aop:config><tx:advice id="txAdvice">    <tx:attributes>        <tx:method name="get*" read-only="true"/>        <tx:method name="save*" rollback-for="DuplicateNameException,UserExistsException" />        <tx:method name="*"/>    </tx:attributes></tx:advice>
After adding this transaction advice, my unit tests failed method-level authorization checks. I never traced the issue to its absolute root cause, but based upon some open tickets against Spring I believe the error was due to a "double-proxy" type issue. The short story is that proxies are final, and Spring can't proxy a proxy...

Regardless of the root cause, I found a reasonable workaround was to revert to one of the "old" (more verbose) methods of transaction demarcation. Here's what my final working configuration looks like:

<bean id="baseTransactionProxy" abstract="true" class="org.springframework.transaction.interceptor.TransactionProxyFactoryBean">    <property name="transactionManager" ref="transactionManager"/>    <property name="transactionAttributes">        <props>            <prop key="get*">PROPAGATION_REQUIRED,readOnly</prop>            <prop key="save*">PROPAGATION_REQUIRED,-DuplicateNameException,-UserExistsException</prop>            <prop key="*">PROPAGATION_REQUIRED</prop>        </props>    </property></bean><bean id="clientManager" parent="baseTransactionProxy">  <property name="target">    <bean class="com.acme.service.impl.ClientManagerImpl">        <sec:intercept-methods access-decision-manager-ref="accessDecisionManager">          <sec:protect method="get" access="AFTER_ACL_READ"/>          <sec:protect method="save" access="ROLE_ADMIN,ACL_WRITE"/>        </sec:intercept-methods>        <constructor-arg ref="clientDao"/>        <property name="mutableAclService" ref="aclService"/>   </bean>  </property></bean>
By leveraging the TransactionProxyFactoryBean directly instead of relying upon the tx:advice and aop:config tags, I was able to wrap my Spring ACL secured methods with declarative transactions. Nice.

Share Comments