You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

添加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会自动根据这些配置生成JwtDecoder Bean,满足安全配置的依赖。
  • 测试环境的application.properties缺少OAuth2资源服务器的JWT相关配置,Spring无法自动创建JwtDecoder Bean,而安全配置中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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.19 17:36:23