Shiro踩坑记(⼀):关于shiro-spring-boot-web-starter⾃动。
。。
⼀)问题描述:
我在⼀个Spring的项⽬中使⽤shiro搭建权限控制框架。主要通过shiro-spring-boot-web-starter包快速集成Shiro。但是项⽬⽆法启动,报没有authorizer的bean的错误:
```
No bean named 'authorizer' available
```
我只好⼜在⾃⼰的Configuration中⼜配置了Authorizer,才能正常启动。
@Configuration
public class ShiroConfig {
@Bean
public Authorizer authorizer(){
return new ModularRealmAuthorizer();
}
}
但是奇怪的明明athorizer是SecurityManager中⼀个重要的组件,为什么没有在shiro starter的Configuration中被声明为Bean?同样
的,Authenticator就没问题?
⼆)明确shiro-spring-boot-web-starter是否有对应的声明
我们在pom⽂件中声明了shiro-spring-boot-web-starter。就从对应的jar包开始起。
⾸先是META-INF中的spring.factories⽂件。我们知道spring-boot-starter都是通过在该⽂件中声明Configuraion来达到集成⾃⾝配置
的⽬的。
org.springframework.boot.autoconfigure.EnableAutoConfiguration = \
org.apache.fig.web.autoconfigure.ShiroWebAutoConfiguration,\
org.apache.fig.web.autoconfigure.ShiroWebFilterConfiguration
上述声明了两个Configration:ShiroWebAutoConfiguration和ShiroWebFilterConfiguration。
ShiroWebFilterConfiguration
先从简单的配置说起,ShiroWebFilterConfiguration是以添加Filter的⽅式来达到authentication的⽬的。这个和我们的问题⽆关,
简单带过。
ShiroWebAutoConfiguration
springboot aop@Configuration
@AutoConfigureBefore(ShiroAutoConfiguration.class)
@ConditionalOnProperty(name = "abled", matchIfMissing = true)
public class ShiroWebAutoConfiguration extends AbstractShiroWebConfiguration {
@Bean
@ConditionalOnMissingBean
@Override
protected AuthenticationStrategy authenticationStrategy() {
return super.authenticationStrategy();
}
@Bean
@ConditionalOnMissingBean
@Override
protected Authenticator authenticator() {
protected Authenticator authenticator() {
return super.authenticator();
}
@Bean
@ConditionalOnMissingBean
@Override
protected Authorizer authorizer() {
return super.authorizer();
}
@Bean
@ConditionalOnMissingBean
@Override
protected SubjectDAO subjectDAO() {
return super.subjectDAO();
}
@Bean
@ConditionalOnMissingBean
@Override
protected SessionStorageEvaluator sessionStorageEvaluator() {
return super.sessionStorageEvaluator();
}
@Bean
@ConditionalOnMissingBean
@Override
protected SubjectFactory subjectFactory() {
return super.subjectFactory();
}
@Bean
@ConditionalOnMissingBean
@Override
protected SessionFactory sessionFactory() {
return super.sessionFactory();
}
@Bean
@ConditionalOnMissingBean
@Override
protected SessionDAO sessionDAO() {
return super.sessionDAO();
}
@Bean
@ConditionalOnMissingBean
@Override
protected SessionManager sessionManager() {
return super.sessionManager();
}
@Bean
@ConditionalOnMissingBean
@Override
protected SessionsSecurityManager securityManager(List<Realm> realms) {        return createSecurityManager();
}
@Bean
@ConditionalOnMissingBean(name = "sessionCookieTemplate")
@Override
protected Cookie sessionCookieTemplate() {
return super.sessionCookieTemplate();
}
}
@Bean
@ConditionalOnMissingBean
@Override
protected RememberMeManager rememberMeManager() {
berMeManager();
}
@Bean
@ConditionalOnMissingBean(name = "rememberMeCookieTemplate")
@Override
protected Cookie rememberMeCookieTemplate() {
berMeCookieTemplate();
}
@Bean
@ConditionalOnMissingBean
@Override
protected ShiroFilterChainDefinition shiroFilterChainDefinition() {
return super.shiroFilterChainDefinition();
}
}
这个配置类将Shiro需要的各组件都声明成了bean,交给容器管理。具体的创建过程都在⽗类Abstract
ShiroWebConfiguration。可以看到确实是有声明authorizer。但是为什么会不到呢?是不是其他的配置⽂件声明了类似的bean,产⽣了影响?
三)继续查其他配置
观察shiro-spring-boot-web-starter的配置⽂件,可以看到它⼜引⽤了shiro-spring-boot-starter包。shrio-spring-boot-starter⼜是⼀个Spring boot starter包,同样通过它的META-INF⽂件,可以知道加⼊了哪些Configuration:
org.springframework.boot.autoconfigure.EnableAutoConfiguration = \
org.apache.shiro.spring.boot.autoconfigure.ShiroBeanAutoConfiguration,\
org.apache.shiro.spring.boot.autoconfigure.ShiroAutoConfiguration,\
org.apache.shiro.spring.boot.autoconfigure.ShiroAnnotationProcessorAutoConfiguration
org.springframework.boot.diagnostics.FailureAnalyzer = \
org.apache.shiro.spring.boot.autoconfigure.ShiroNoRealmConfiguredFailureAnalyzer
最后⼀个⽂件是判断项⽬中不存在Realm时,抛出异常。前⾯是我们需要关注的配置⽂件。
ShiroAnnotationProcessorAutoConfiguration
该配置主要是通过AOP的⽅式实现authorization的功能。
ShiroBeanAutoConfiguraion
主要是通过添加BeanPostProcessor,在Shiro相关的Bean初始化时,做⼀些额外的操作。
ShiroAutoConfiguration
@Configuration
@SuppressWarnings("SpringFacetCodeInspection")
@ConditionalOnProperty(name = "abled", matchIfMissing = true)
public class ShiroAutoConfiguration extends AbstractShiroConfiguration {
@Bean
@ConditionalOnMissingBean
@Override
protected AuthenticationStrategy authenticationStrategy() {
return super.authenticationStrategy();
}
@Bean
@Bean
@ConditionalOnMissingBean
@Override
protected Authenticator authenticator() {
return super.authenticator();
}
@Bean
@ConditionalOnMissingBean
@Override
protected Authorizer authorizer() {
return super.authorizer();
}
@Bean
@ConditionalOnMissingBean
@Override
protected SubjectDAO subjectDAO() {
return super.subjectDAO();
}
@Bean
@ConditionalOnMissingBean
@Override
protected SessionStorageEvaluator sessionStorageEvaluator() {
return super.sessionStorageEvaluator();
}
@Bean
@ConditionalOnMissingBean
@Override
protected SubjectFactory subjectFactory() {
return super.subjectFactory();
}
@Bean
@ConditionalOnMissingBean
@Override
protected SessionFactory sessionFactory() {
return super.sessionFactory();
}
@Bean
@ConditionalOnMissingBean
@Override
protected SessionDAO sessionDAO() {
return super.sessionDAO();
}
@Bean
@ConditionalOnMissingBean
@Override
protected SessionManager sessionManager() {
return super.sessionManager();
}
@Bean
@ConditionalOnMissingBean
@Override
protected SessionsSecurityManager securityManager(List<Realm> realms) {        return super.securityManager(realms);
}
@Bean
@ConditionalOnResource(resources = "classpath:shiro.ini")
protected Realm iniClasspathRealm() {
protected Realm iniClasspathRealm() {
return iniRealmFromLocation("classpath:shiro.ini");
}
@Bean
@ConditionalOnResource(resources = "classpath:META-INF/shiro.ini")
protected Realm iniMetaInfClasspathRealm() {
return iniRealmFromLocation("classpath:META-INF/shiro.ini");
}
@Bean
@ConditionalOnMissingBean(Realm.class)
protected Realm missingRealm() {
throw new NoRealmBeanConfiguredException();
}
}
⼤致内容其实和ShiroWebAutoConfiguration很类似,只是ShiroWebAutoConfiguration将⼀些组件替
换成了WEB环境相关的组件。但是ShiroWebAutoConfiguration声明了它的配置要在ShiroAutoConfiguration之前,⽽且根据ConditionalOnMissingBean的条件,得出Bean的配置应该是以ShiroWebAutoConfiguration中声明的为准。但是死马当活马医,配置⽂件中添加abled为false的条件,再试试。。。果然还是不⾏。
四)DEBUG⼤法好
毫⽆办法的办法就是DEBUG⼤法。
⾸先从Configuration中⽣命的Bean是如何被容器加载的过程⼊⼿,到了ConfigurationClassPostProcessor。同样是⼀个PostProcessor,猜想应该是在configuration bean的后置处理中进⾏了@Bean⽅法的解析。
主要的处理过程在processConfigBeanDefinition这个⽅法中,对这个⽅法做个简单的说明
/**
* Build and validate a configuration model based on the registry of
* {@link Configuration} classes.
*/
public void processConfigBeanDefinitions(BeanDefinitionRegistry registry) {
List<BeanDefinitionHolder> configCandidates = new ArrayList<>();
//获取registry中的bean definition
String[] candidateNames = BeanDefinitionNames();
for (String beanName : candidateNames) {
BeanDefinition beanDef = BeanDefinition(beanName);
//bean definition 有configuration的属性,说明已经被解析处理过
if (ConfigurationClassUtils.isFullConfigurationClass(beanDef) ||
ConfigurationClassUtils.isLiteConfigurationClass(beanDef)) {
if (logger.isDebugEnabled()) {
logger.debug("Bean definition has already been processed as a configuration class: " + beanDef);
}
}
//判断是否是configuration的bean,是则加⼊候选
else if (ConfigurationClassUtils.checkConfigurationClassCandidate(beanDef, adataReaderFactory)) {
configCandidates.add(new BeanDefinitionHolder(beanDef, beanName));
}
}
// 如果没有发现候选者,则返回
if (configCandidates.isEmpty()) {
return;
}
// 排序
configCandidates.sort((bd1, bd2) -> {
int i1 = BeanDefinition());
int i2 = BeanDefinition());

版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。