项目背景:
服务基于springboot搭建,shiro做认证鉴权,本地启动正常,打包正常,服务器启动正常,采用jenkins打包部署服务,偶尔出现问题
原因分析:
因为问题是偶现的,当时觉得是环境问题导致,有的服务器可以启动,有的服务器无法启动,报错是偶现的
初步排查了环境,jdk,port,mvn,jenkins服务器配置,都没问题
排查日志:
2020-05-13 22:07:48.066 INFO 876 --- [main] com.*.SpringBootApplication : Starting SpringBootApplication v1.0.0.0-SNAPSHOT on server with PID 876 (/opt/server/ms-server.jar started by root in /opt/server)
2020-05-13 22:07:48.072 DEBUG 876 --- [main] com.*.SpringBootApplication : Running with Spring Boot v2.2.2.RELEASE, Spring v5.2.2.RELEASE
2020-05-13 22:07:48.072 INFO 876 --- [main] com.*.SpringBootApplication : The following profiles are active: dev
2020-05-13 22:07:51.969 INFO 876 --- [main] .s.d.r.c.RepositoryConfigurationDelegate : Multiple Spring Data modules found, entering strict repository configuration mode!
2020-05-13 22:07:51.976 INFO 876 --- [main] .s.d.r.c.RepositoryConfigurationDelegate : Bootstrapping Spring Data Elasticsearch repositories in DEFAULT mode.
2020-05-13 22:07:52.113 INFO 876 --- [main] .s.d.r.c.RepositoryConfigurationDelegate : Finished Spring Data repository scanning in 114ms. Found 0 Elasticsearch repository interfaces.
2020-05-13 22:07:52.134 INFO 876 --- [main] .s.d.r.c.RepositoryConfigurationDelegate : Multiple Spring Data modules found, entering strict repository configuration mode!
2020-05-13 22:07:52.134 INFO 876 --- [main] .s.d.r.c.RepositoryConfigurationDelegate : Bootstrapping Spring Data Reactive Elasticsearch repositories in DEFAULT mode.
2020-05-13 22:07:52.177 INFO 876 --- [main] .s.d.r.c.RepositoryConfigurationDelegate : Finished Spring Data repository scanning in 42ms. Found 0 Reactive Elasticsearch repository interfaces.
2020-05-13 22:07:52.240 INFO 876 --- [main] .s.d.r.c.RepositoryConfigurationDelegate : Multiple Spring Data modules found, entering strict repository configuration mode!
2020-05-13 22:07:52.284 INFO 876 --- [main] .s.d.r.c.RepositoryConfigurationDelegate : Bootstrapping Spring Data Redis repositories in DEFAULT mode.
2020-05-13 22:07:52.357 INFO 876 --- [main] .s.d.r.c.RepositoryConfigurationDelegate : Finished Spring Data repository scanning in 44ms. Found 0 Redis repository interfaces.
2020-05-13 22:07:53.633 INFO 876 --- [main] trationDelegate$BeanPostProcessorChecker : Bean 'webConfig' of type [com.*.config.WebConfig$$EnhancerBySpringCGLIB$$323d34e4]
is not eligible for getting processed by all BeanPostProcessors (for example: not eligible for auto-proxying)
2020-05-13 22:07:54.038 INFO 876 --- [main] trationDelegate$BeanPostProcessorChecker : Bean 'org.springframework.transaction.annotation.ProxyTransactionManagementConfiguration' of type [org.springframework.transaction.annotation.ProxyTransactionManagementConfiguration]
is not eligible for getting processed by all BeanPostProcessors (for example: not eligible for auto-proxying)
2020-05-13 22:07:54.442 INFO 876 --- [main] trationDelegate$BeanPostProcessorChecker : Bean 'redisConfig' of type [com.*.base.redis.RedisConfig$$EnhancerBySpringCGLIB$$4b225e87
is not eligible for getting processed by all BeanPostProcessors (for example: not eligible for auto-proxying)
2020-05-13 22:07:54.479 INFO 876 --- [main] trationDelegate$BeanPostProcessorChecker : Bean 'shiroConfig' of type [com.*.config.ShiroConfig$$EnhancerBySpringCGLIB$$e73c6be1]
is not eligible for getting processed by all BeanPostProcessors (for example: not eligible for auto-proxying)
2020-05-13 22:07:54.543 WARN 876 --- [main] ConfigServletWebServerApplicationContext : Exception encountered during context initialization - cancelling refresh attempt:
org.springframework.beans.factory.BeanCreationException:
Error creating bean with name 'objectMapperConfigurer' defined in class path resource [springfox/documentation/spring/web/SpringfoxWebMvcConfiguration.class]:
BeanPostProcessor before instantiation of bean failed;
nested exception is org.springframework.beans.factory.BeanCreationException:
Error creating bean with name 'druidConfig' defined in URL [jar:file:/opt/server/ms-server.jar!/BOOT-INF/classes!/com/*/config/DruidConfig.class]:
BeanPostProcessor before instantiation of bean failed;
nested exception is org.springframework.beans.factory.BeanCreationException:
Error creating bean with name 'authorizationAttributeSourceAdvisor' defined in class path resource [com/*/config/ShiroConfig.class]:
Bean instantiation via factory method failed; nested exception is org.springframework.beans.BeanInstantiationException:
Failed to instantiate [org.apache.shiro.spring.security.interceptor.AuthorizationAttributeSourceAdvisor]:
Factory method 'authorizationAttributeSourceAdvisor' threw exception; nested exception is org.springframework.beans.factory.BeanCreationException:
Error creating bean with name 'securityManager' defined in class path resource [com/*/config/ShiroConfig.class]:
Bean instantiation via factory method failed;
nested exception is org.springframework.beans.BeanInstantiationException:
Failed to instantiate [org.apache.shiro.mgt.SecurityManager]:
Factory method 'securityManager' threw exception;
nested exception is org.springframework.beans.factory.UnsatisfiedDependencyException:
Error creating bean with name 'customRealm':
Unsatisfied dependency expressed through field 'managerBiz';
nested exception is org.springframework.beans.factory.UnsatisfiedDependencyException:
Error creating bean with name 'shirFilter' defined in class path resource [com/*/config/ShiroConfig.class]:
Unsatisfied dependency expressed through method 'shirFilter' parameter 0;
nested exception is org.springframework.beans.factory.BeanCurrentlyInCreationException:
Error creating bean with name 'securityManager': Requested bean is currently in creation:
Is there an unresolvable circular reference?
2020-05-13 22:07:54.556 INFO 876 --- [main] ConditionEvaluationReportLoggingListener :
Error starting ApplicationContext. To display the conditions report re-run your application with 'debug' enabled.
2020-05-13 22:07:54.571 ERROR 876 --- [main] o.s.b.d.LoggingFailureAnalysisReporter :
***************************
APPLICATION FAILED TO START
***************************
Description:
The dependencies of some of the beans in the application context form a cycle:
objectMapperConfigurer defined in class path resource [springfox/documentation/spring/web/SpringfoxWebMvcConfiguration.class]
↓
druidConfig defined in URL [jar:file:/opt/server/ms-server.jar!/BOOT-INF/classes!/com/*/config/DruidConfig.class]
↓
authorizationAttributeSourceAdvisor defined in class path resource [com/*/config/ShiroConfig.class]
┌─────┐
| securityManager defined in class path resource [com/*/config/ShiroConfig.class]
↑ ↓
| customRealm (field private com.*.basic.biz.IXBiz com.*.basic.security.BaseAuthRealm.managerBiz)
↑ ↓
| shirFilter defined in class path resource [com/*/config/ShiroConfig.class]
└─────┘
排查发现 虽然是偶现的打包问题 本地起服务虽然可以起来 但这里报循环依赖问题 查看日志发现
Failed to instantiate [org.apache.shiro.spring.security.interceptor.AuthorizationAttributeSourceAdvisor]:
Factory method 'authorizationAttributeSourceAdvisor' threw exception; nested exception is org.springframework.beans.factory.BeanCreationException:
Error creating bean with name 'securityManager' defined in class path resource [com/*/config/ShiroConfig.class]:
┌─────┐
| securityManager defined in class path resource [com/*/config/ShiroConfig.class]
↑ ↓
| customRealm (field private com.*.basic.biz.IXBiz com.*.basic.security.BaseAuthRealm.managerBiz)
↑ ↓
| shirFilter defined in class path resource [com/*/config/ShiroConfig.class]
└─────┘
在此处创建Bean失败了~
问题定位:
附上问题代码:
package com.*.config;
import com.*.basic.security.BaseAuthRealm;
import org.apache.shiro.mgt.SecurityManager;
import org.apache.shiro.spring.security.interceptor.AuthorizationAttributeSourceAdvisor;
import org.apache.shiro.spring.web.ShiroFilterFactoryBean;
import org.apache.shiro.web.mgt.DefaultWebSecurityManager;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import java.util.LinkedHashMap;
import java.util.Map;
@Configuration
public class ShiroConfig {
@Value("${path}")
private String path;
@Bean
public ShiroFilterFactoryBean shirFilter(SecurityManager securityManager) {
ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean();
// 必须设置 SecurityManager
shiroFilterFactoryBean.setSecurityManager(securityManager);
// setLoginUrl 如果不设置值,默认会自动寻找Web工程根目录下的"/login.jsp"页面 或 "/login" 映射
shiroFilterFactoryBean.setLoginUrl(path + "/index");
// 设置无权限时跳转的 url;
shiroFilterFactoryBean.setUnauthorizedUrl(path + "/404.html");
// 设置拦截器
Map filterChainDefinitionMap = new LinkedHashMap<>();
// 游客,开发权限
filterChainDefinitionMap.put("/static/**", "anon");
filterChainDefinitionMap.put("/html/**", "anon");
// 开放登陆接口
filterChainDefinitionMap.put(path + "/index", "anon");
// 其余接口一律拦截
// 主要这行代码必须放在所有权限设置的最后,不然会导致所有 url 都被拦截
filterChainDefinitionMap.put(path + "/**", "authc");
shiroFilterFactoryBean.setFilterChainDefinitionMap(filterChainDefinitionMap);
return shiroFilterFactoryBean;
}
/**
* 注入 securityManager
*/
@Bean
public SecurityManager securityManager() {
DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
// 设置realm
securityManager.setRealm(customRealm());
return securityManager;
}
@Bean
public AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor() {
AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor = new AuthorizationAttributeSourceAdvisor();
authorizationAttributeSourceAdvisor.setSecurityManager(securityManager());
return authorizationAttributeSourceAdvisor;
}
/**
* 自定义身份认证 realm;
* 必须写这个类,并加上 @Bean 注解,目的是注入 自定义Realm, 否则会影响 自定义Realm类 中其他类的依赖注入
*/
@Bean
public BaseAuthRealm customRealm() {
return new BaseAuthRealm();
}
}
此处问题代码:
@Bean
public AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor() {
AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor = new AuthorizationAttributeSourceAdvisor();
authorizationAttributeSourceAdvisor.setSecurityManager(securityManager());
return authorizationAttributeSourceAdvisor;
}
原因分析:
这个bean注册时,调用了securityManager()方法,此处这个方法被多次调用,原则上应该是引用而不是新建一个Bean,此处Bean应该是单例的
代码修复:
@Bean
@ConditionalOnMissingBean
public AuthorizationAttributeSourceAdvisor
authorizationAttributeSourceAdvisor(SecurityManager securityManager) {
AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor = new
AuthorizationAttributeSourceAdvisor();
authorizationAttributeSourceAdvisor.setSecurityManager(securityManager);
return authorizationAttributeSourceAdvisor;
}
附上修复后的代码:
package com.*.config;
import com.*.basic.security.BaseAuthRealm;
import org.apache.shiro.mgt.SecurityManager;
import org.apache.shiro.spring.security.interceptor.AuthorizationAttributeSourceAdvisor;
import org.apache.shiro.spring.web.ShiroFilterFactoryBean;
import org.apache.shiro.web.mgt.DefaultWebSecurityManager;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import java.util.LinkedHashMap;
import java.util.Map;
@Configuration
public class ShiroConfig {
@Value("${path}")
private String path;
@Bean
public ShiroFilterFactoryBean shirFilter(SecurityManager securityManager) {
ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean();
// 必须设置 SecurityManager
shiroFilterFactoryBean.setSecurityManager(securityManager);
// setLoginUrl 如果不设置值,默认会自动寻找Web工程根目录下的"/login.jsp"页面 或 "/login" 映射
shiroFilterFactoryBean.setLoginUrl(path + "/index");
// 设置无权限时跳转的 url;
shiroFilterFactoryBean.setUnauthorizedUrl(path + "/404.html");
// 设置拦截器
Map filterChainDefinitionMap = new LinkedHashMap<>();
// 游客,开发权限
filterChainDefinitionMap.put("/static/**", "anon");
filterChainDefinitionMap.put("/html/**", "anon");
// 开放登陆接口
filterChainDefinitionMap.put(path + "/index", "anon");
// 其余接口一律拦截
// 主要这行代码必须放在所有权限设置的最后,不然会导致所有 url 都被拦截
filterChainDefinitionMap.put(path + "/**", "authc");
shiroFilterFactoryBean.setFilterChainDefinitionMap(filterChainDefinitionMap);
return shiroFilterFactoryBean;
}
/**
* 注入 securityManager
*/
@Bean
public SecurityManager securityManager() {
DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
// 设置realm
securityManager.setRealm(customRealm());
return securityManager;
}
@Bean
@ConditionalOnMissingBean
public AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor(SecurityManager securityManager) {
AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor = new AuthorizationAttributeSourceAdvisor();
authorizationAttributeSourceAdvisor.setSecurityManager(securityManager);
return authorizationAttributeSourceAdvisor;
}
/**
* 自定义身份认证 realm;
* 必须写这个类,并加上 @Bean 注解,目的是注入 自定义Realm, 否则会影响 自定义Realm类 中其他类的依赖注入
*/
@Bean
public BaseAuthRealm customRealm() {
return new BaseAuthRealm();
}
}
问题解决 jenkins部署/本地打包/mvn都不会出现问题了~