在同一個 Service 方法上同時標註 @Transactional(事務管理)與 @RequiresPermissions(Shiro 權限控制)時,發現權限註解不生效,方法可被無權限用戶直接調用。
Spring 默認優先使用 JDK 動態代理(基於接口)。當目標類實現了接口時,Spring 會為其創建接口代理對象(如 ExampleServiceImpl$$)。
Proxy Object One(實現了目標接口)。Proxy Object Two(繼承自 Proxy Object One)。@RequiresPermissions 的攔截器掛載在 Proxy Object Two 上,而外部調用持有的是 Proxy Object One 的引用。調用鏈繞過了第二層代理,導致權限檢查失效。將兩個切面都基於同一個 類代理 構建,避免雙重代理嵌套。
<!-- 強制使用 CGLIB 代理 -->
<aop:config proxy-target-class="true">
<aop:advisor advice-ref="permissionAdvice" pointcut="execution(* com.example.service..*(..))" />
</aop:config>
<tx:annotation-driven proxy-target-class="true" transaction-manager="transactionManager" />
關鍵屬性:proxy-target-class="true" 強制 Spring 使用 CGLIB 為目標類創建子類代理,而非 JDK 接口代理。
配置修改後,重啟應用,@RequiresPermissions 重新生效,未授權用戶調用將拋出 UnauthorizedException。
final 方法,若目標類含 final 方法需謹慎評估。proxy-target-class="true",或通過 spring.aop.proxy-target-class=true(Spring Boot)統一為類代理。@Order 或 Ordered 顯式指定切面執行順序,避免隱式排序導致的不可預期行為。Spring AOP 的代理機制靈活強大,但混合使用接口代理與類代理時易產生「代理鏈斷裂」問題。通過 proxy-target-class="true 統一代理模式,可徹底解決 @Transactional 與 @RequiresPermissions 等多切面共存時的失效難題,構建穩健的切面編程架構。