问题
加入Shiro,导致了Spring AOP失效,Spring注解@Aysnc和@Cacheable没有效果
Maven 导入Shiro依赖
SpringBoot导入Shiro很简单只需要导入shiro-spring-boot-starter
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-spring-boot-starter</artifactId>
<version>1.5.2</version>
</dependency>
复制代码
导入shiro-spring-boot-starter只需要很少的配置就能让Shiro工作 它有两个自动装配类 ShiroAutoConfiguration 自动配置一些Shiro相关的配置类 ShiroAnnotationProcessorAutoConfiguration 这个类可以让Shiro注解生效 例如:@RequiresAuthentication和RequiresPermissions
但是就是这个ShiroAnnotationProcessorAutoConfiguration会和Spring AOP起冲突
启动SpringBoot会出现这个错误
***************************
APPLICATION FAILED TO START
***************************
Description:
The bean 'xxxx' could not be injected as a 'com.xxx.xxx' because it is a JDK dynamic proxy that implements:
com.egzosn.pay.common.api.PayMessageHandler
Action:
Consider injecting the bean as one of its interfaces or forcing the use of CGLib-based proxies by setting proxyTargetClass=true on @EnableAsync and/or @EnableCaching.
复制代码
这个时候你需要在SpringBootApplication排除ShiroAnnotationProcessorAutoConfiguration
@SpringBootApplication(exclude = {ShiroAnnotationProcessorAutoConfiguration.class})
复制代码
这样就不会报错了,不过有些人在在自己的Shiro配置类(ShiroConfiguration)
加入了
@Bean
@DependsOn("lifecycleBeanPostProcessor")
public DefaultAdvisorAutoProxyCreator defaultAdvisorAutoProxyCreator() {
DefaultAdvisorAutoProxyCreator defaultAdvisorAutoProxyCreator = new DefaultAdvisorAutoProxyCreator();
defaultAdvisorAutoProxyCreator.setProxyTargetClass(true);
return defaultAdvisorAutoProxyCreator;
}
复制代码
所以不排除ShiroAnnotationProcessorAutoConfiguration也不会报错,但是在这里配置的动态代理会导致Spring AOP失效 导致Spring AOP失效 会后果是你使用了@EnableCaching和@EnableAsync都失效,比如你在方法上使用@Async它还是会使用同一个线程,导致达不到异步执行的效果
我的完整解决方案
@Configuration
public class ShiroConfiguration {
@Bean("securityManager")
public DefaultWebSecurityManager getManager(JwtRealm realm) {
DefaultWebSecurityManager manager = new DefaultWebSecurityManager();
// 使用自己的realm
manager.setRealm(realm);
/*
* 关闭shiro自带的session,详情见文档
* http://shiro.apache.org/session-management.html#SessionManagement-StatelessApplications%28Sessionless%29
*/
DefaultSubjectDAO subjectDAO = new DefaultSubjectDAO();
DefaultSessionStorageEvaluator defaultSessionStorageEvaluator = new DefaultSessionStorageEvaluator();
// defaultSessionStorageEvaluator.setSessionStorageEnabled(false);
subjectDAO.setSessionStorageEvaluator(defaultSessionStorageEvaluator);
manager.setSubjectDAO(subjectDAO);
return manager;
}
@Bean("shiroFilter")
public ShiroFilterFactoryBean factory(DefaultWebSecurityManager securityManager, AppProperties appProperties) {
ShiroFilterFactoryBean factoryBean = new ShiroFilterFactoryBean();
// 添加自己的过滤器并且取名为jwt
Map<String, Filter> filterMap= new HashMap<>(10);
filterMap.put("jwt", new JwtTokenFilter());
factoryBean.setFilters(filterMap);
factoryBean.setSecurityManager(securityManager);
factoryBean.setUnauthorizedUrl("/401");
/*
* 自定义url规则
* http://shiro.apache.org/web.html#urls-
*/
Map<String, String> filterRuleMap = new HashMap<>(10);
// 所有请求通过我们自己的JWT Filter 全部放行 x.put("/**", "anon");
filterRuleMap.put("/**", "jwt");
if (appProperties != null && CollUtil.isNotEmpty(appProperties.getIgnoreingUrls())) {
for (String ignoreingUrl : appProperties.getIgnoreingUrls()) {
filterRuleMap.put(ignoreingUrl, "anon");
}
}
factoryBean.setFilterChainDefinitionMap(filterRuleMap);
return factoryBean;
}
/**
* 下面的代码是添加注解支持
*/
// @Bean
// @ConditionalOnClass
// @DependsOn("lifecycleBeanPostProcessor")
// public DefaultAdvisorAutoProxyCreator defaultAdvisorAutoProxyCreator() {
// DefaultAdvisorAutoProxyCreator defaultAdvisorAutoProxyCreator = new DefaultAdvisorAutoProxyCreator();
// defaultAdvisorAutoProxyCreator.setProxyTargetClass(true);
// return defaultAdvisorAutoProxyCreator;
// }
// LifecycleBeanPostProcessor会导致Spring AOP失效
// @Bean
// public LifecycleBeanPostProcessor lifecycleBeanPostProcessor() {
// return new LifecycleBeanPostProcessor();
// }
//
@Bean
public AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor(DefaultWebSecurityManager securityManager) {
AuthorizationAttributeSourceAdvisor advisor = new AuthorizationAttributeSourceAdvisor();
advisor.setSecurityManager(securityManager);
return advisor;
}
}
复制代码
DefaultAdvisorAutoProxyCreator和LifecycleBeanPostProcessor我都注释了,这两个类会导致AOP失效
因为已经加入了Spring AOP所以不需要使用DefaultAdvisorAutoProxyCreator
我们只要在配置文件 application.properties 加入 spring.aop.proxy-target-class=true 然后在SpringBoot启动类注解排除 ShiroAnnotationProcessorAutoConfiguration
@SpringBootApplication(exclude = {ShiroAnnotationProcessorAutoConfiguration.class})
复制代码
这样SPring AOP就正常了,Shiro注册也可以使用了.
总结
- 导致AOP试下的坑之一 ShiroAnnotationProcessorAutoConfiguration 是因为加入了
@Bean
@DependsOn({"lifecycleBeanPostProcessor"})
@ConditionalOnMissingBean
public DefaultAdvisorAutoProxyCreator defaultAdvisorAutoProxyCreator() {
return super.defaultAdvisorAutoProxyCreator();
}
复制代码
加入DefaultAdvisorAutoProxyCreator会导致Spring AOP失效,所有@Service @Component的类都不是动态代理对象 导致@Aysnc和@Cacheable等注解的失效
- 网络上很多SpringBoot加入Shiro的文章配置类都加入了
@Bean
@ConditionalOnClass
@DependsOn("lifecycleBeanPostProcessor")
public DefaultAdvisorAutoProxyCreator defaultAdvisorAutoProxyCreator() {
DefaultAdvisorAutoProxyCreator defaultAdvisorAutoProxyCreator = new DefaultAdvisorAutoProxyCreator();
defaultAdvisorAutoProxyCreator.setProxyTargetClass(true);
}
@Bean
public LifecycleBeanPostProcessor lifecycleBeanPostProcessor() {
return new LifecycleBeanPostProcessor();
}
复制代码
LifecycleBeanPostProcessor 和 DefaultAdvisorAutoProxyCreator都会导致Spring AOP失效, 如果你想让Shiro注解生效加入AuthorizationAttributeSourceAdvisor类和spring配置spring.aop.proxy-target-class=true