转载

Spring Boot 加入Shiro导致AOP失效的坑

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注册也可以使用了.

总结

  1. 导致AOP试下的坑之一 ShiroAnnotationProcessorAutoConfiguration 是因为加入了
@Bean
@DependsOn({"lifecycleBeanPostProcessor"})
@ConditionalOnMissingBean
public DefaultAdvisorAutoProxyCreator defaultAdvisorAutoProxyCreator() {
        return super.defaultAdvisorAutoProxyCreator();
}
复制代码

加入DefaultAdvisorAutoProxyCreator会导致Spring AOP失效,所有@Service @Component的类都不是动态代理对象 导致@Aysnc和@Cacheable等注解的失效

  1. 网络上很多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

原文  https://juejin.im/post/5e96715d6fb9a03c585c0616
正文到此结束
Loading...