添加JUnit测试后Spring Boot应用启动失败,求解决方案
问题描述
给Spring Boot应用添加JUnit测试后,应用无法启动。存在一个自定义JwtCustomValidator类用于JWT校验,不确定是否因此引发问题。安全配置类通过antMatchers配置接口权限,并启用了oauth2ResourceServer的jwt模式,启动时抛出错误,提示找不到org.springframework.security.oauth2.jwt.JwtDecoder类型的Bean。
问题原因分析
- 主环境的
application.properties中配置了spring.security.oauth2.resourceserver.jwt.issuer-uri和spring.security.oauth2.resourceserver.jwt.jwk-set-uri,Spring Security会自动根据这些配置生成JwtDecoderBean,满足安全配置的依赖。 - 测试环境的
application.properties缺少OAuth2资源服务器的JWT相关配置,Spring无法自动创建JwtDecoderBean,而安全配置中oauth2ResourceServer(oauth2 -> oauth2.jwt())必须依赖该Bean,因此启动失败。 - 自定义
JwtCustomValidator本身不是问题根源,它只是一个普通的组件Bean,不会导致JwtDecoder缺失。
解决办法
方案1:测试环境添加JWT配置
在测试环境的application.properties中添加最小化的JWT配置,比如使用一个本地JWKS或者模拟的配置:
spring.security.oauth2.resourceserver.jwt.issuer-uri=http://localhost:8080 spring.security.oauth2.resourceserver.jwt.jwk-set-uri=http://localhost:8080/.well-known/jwks.json
如果没有真实的JWKS服务,可以用测试框架提供的模拟实现。
方案2:测试时提供模拟JwtDecoder Bean
在测试类中添加@MockBean来模拟JwtDecoder,避免真实的配置依赖:
@SpringBootTest public class YourTestClass { @MockBean private JwtDecoder jwtDecoder; // 测试用例 }
或者在测试专用的配置类中定义一个模拟的JwtDecoder:
@Configuration public class TestSecurityConfig { @Bean public JwtDecoder jwtDecoder() { // 创建一个模拟的JwtDecoder,返回固定的JWT对象 return token -> Jwt.withTokenValue("mock-token") .header("alg", "HS256") .claim("roles", List.of("ALLOW-allowedtraffic")) .audience(List.of("mock-audience")) .build(); } }
然后在测试类中引入该配置:
@SpringBootTest(classes = {YourApplication.class, TestSecurityConfig.class}) public class YourTestClass { // 测试用例 }
方案3:测试时禁用安全校验(仅适用于不需要安全验证的测试)
如果测试用例不需要验证JWT,可以在测试类中覆盖安全配置,关闭安全校验:
@SpringBootTest @AutoConfigureMockMvc(addFilters = false) public class YourTestClass { // 测试用例 }
或者自定义一个测试安全配置,允许所有请求:
@Configuration @EnableWebSecurity public class TestSecurityConfig extends WebSecurityConfigurerAdapter { @Override protected void configure(HttpSecurity http) throws Exception { http.authorizeRequests().anyRequest().permitAll(); } }
相关代码与配置
自定义JwtCustomValidator类
@Component public class JwtCustomValidator { private static final Logger logger = LoggerFactory.getLogger(LogDeletionJob.class); private static final String TAG = "JwtCustomValidator"; public void validateAudience(Jwt jwt) throws Exception { if (!jwt.getAudience().contains(System.getenv("oauthAudience"))) { logger.warn(TAG, "AUDIENCE NOT AUTHORIZED!" + jwt.getAudience()); throw new Exception("wrong audience"); } logger.info(TAG, "audience validated"); } public void validateRole(final Jwt jwt, final String role) throws Exception { if (!((List<String>) jwt.getClaim("roles")).contains(role)) { logger.warn(TAG, "ROLE NOT AUTHORIZED!" + jwt.getClaim("roles").toString()); throw new Exception("Not sufficient role, UnAuthorized"); } logger.info(TAG, "role validated"); } }
安全配置类
@EnableWebSecurity(debug = false) @Configuration public class ResourceServerSecurityConfigs extends WebSecurityConfigurerAdapter { @Override protected void configure(HttpSecurity http) throws Exception { http.authorizeRequests(authz -> authz .antMatchers(HttpMethod.GET, "api/v1/books/**").hasAuthority("ALLOW-allowedtraffic") .antMatchers(HttpMethod.POST, "api/v1/books/**").hasAuthority("ALLOW-allowedtraffic") .antMatchers(HttpMethod.PUT, "api/v1/books/**").hasAuthority("ALLOW-allowedtraffic") .antMatchers(HttpMethod.DELETE, "api/v1/books/**").hasAuthority("ALLOW-allowedtraffic") .antMatchers(HttpMethod.GET, "api/v1/lending/**").hasAuthority("ALLOW-allowedtraffic") .antMatchers(HttpMethod.POST, "api/v1/lending/**").hasAuthority("ALLOW-allowedtraffic") .antMatchers(HttpMethod.PUT, "api/v1/lending/**").hasAuthority("ALLOW-allowedtraffic") .antMatchers(HttpMethod.DELETE, "api/v1/lending/**").hasAuthority("ALLOW-allowedtraffic") .anyRequest().authenticated()) .oauth2ResourceServer(oauth2 -> oauth2.jwt()); } }
启动报错信息
*************************** APPLICATION FAILED TO START *************************** Description: Method springSecurityFilterChain in org.springframework.security.config.annotation.web.configuration.WebSecurityConfiguration required a bean of type 'org.springframework.security.oauth2.jwt.JwtDecoder' that could not be found. Action: Consider defining a bean of type 'org.springframework.security.oauth2.jwt.JwtDecoder' in your configuration. ...(省略部分堆栈信息)
主环境application.properties
logging.level.org.springframework.jdbc.core=${logLvl} spring.datasource.url=${dbConnStr} spring.datasource.username=${dbUser} spring.datasource.password=${dbPw} spring.datasource.driverClassName=com.microsoft.sqlserver.jdbc.SQLServerDriver spring.jpa.show-sql=true spring.jpa.hibernate.dialect=com.backend.hibernat.CustSQLServerDialect spring.jpa.hibernate.ddl-auto = none server.port=8081 myAppPath=${myAppPath} tenantId=123-tenant-id-456 spring.security.oauth2.resourceserver.jwt.issuer-uri=http://issues-uri spring.security.oauth2.resourceserver.jwt.jwk-set-uri=https://keys
测试环境application.properties
spring.jpa.show-sql=true spring.jpa.properties.hibernate.format_sql=true spring.datasource.driverClassName=org.h2.Driver spring.datasource.url=jdbc:h2:mem:db;DB_CLOSE_DELAY=-1 spring.datasource.username=sa spring.datasource.password=sa spring.h2.console.enabled=false
内容的提问来源于stack exchange,提问作者CompileNow
相关产品推荐
相关产品推荐

