如何为Micronaut的JWKS端点配置WireMock动态端口避免测试冲突?
解决Micronaut测试中JWKS随机端口配置问题及替代方案
一、WireMock随机端口的正确配置方式
你之前在application.yaml中用${wiremock.server.port}无法解析,核心原因是Micronaut的配置加载时机早于WireMock启动,导致表达式无法获取到动态端口。正确的做法是在测试代码中动态注入配置:
- 启动WireMock时指定随机端口(用
0表示) - 在测试初始化阶段,将WireMock的实际端口拼接成JWKS URL,设置到Micronaut的环境配置中
示例代码(Java):
import com.github.tomakehurst.wiremock.WireMockServer; import com.github.tomakehurst.wiremock.client.WireMock; import io.micronaut.context.env.Environment; import io.micronaut.test.extensions.junit5.annotation.MicronautTest; import jakarta.inject.Inject; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import static com.github.tomakehurst.wiremock.client.WireMock.aResponse; import static com.github.tomakehurst.wiremock.client.WireMock.get; import static com.github.tomakehurst.wiremock.client.WireMock.urlEqualTo; @MicronautTest class JwtValidationTest { @Inject WireMockServer wireMockServer; @Inject Environment environment; @BeforeEach void setupJwksEndpoint() { // 动态生成JWKS URL并注入Micronaut配置 String jwksUrl = String.format("http://localhost:%d/keys", wireMockServer.port()); environment.put("micronaut.security.jwt.jwks.url", jwksUrl); // 配置WireMock返回合法的JWKS响应 wireMockServer.stubFor(get(urlEqualTo("/keys")) .willReturn(aResponse() .withHeader("Content-Type", "application/json") .withBody("{\"keys\": [你的JWKS密钥内容]}"))); } @Test void testJwtValidation() { // 测试逻辑:生成使用WireMock中密钥签名的JWT,调用接口验证 } }
注意:需要确保WireMock的依赖正确引入,并且测试类中已配置WireMock的启动(比如用@WireMockTest注解或手动初始化WireMockServer)。
二、不使用WireMock的替代方案
方案1:本地生成密钥对,跳过JWKS依赖
直接在测试中生成RSA密钥对,配置Micronaut使用本地公钥验证JWT,用私钥签名测试用的JWT,完全不需要外部JWKS服务:
import io.micronaut.context.env.Environment; import io.micronaut.test.extensions.junit5.annotation.MicronautTest; import jakarta.inject.Inject; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import java.security.KeyPair; import java.security.KeyPairGenerator; import java.security.NoSuchAlgorithmException; import java.security.interfaces.RSAPrivateKey; import java.security.interfaces.RSAPublicKey; import io.jsonwebtoken.Jwts; import io.jsonwebtoken.SignatureAlgorithm; @MicronautTest class LocalKeyJwtTest { @Inject Environment environment; private RSAPrivateKey privateKey; @BeforeEach void setupLocalKeys() throws NoSuchAlgorithmException { // 生成RSA密钥对 KeyPairGenerator keyGen = KeyPairGenerator.getInstance("RSA"); keyGen.initialize(2048); KeyPair keyPair = keyGen.generateKeyPair(); RSAPublicKey publicKey = (RSAPublicKey) keyPair.getPublic(); privateKey = (RSAPrivateKey) keyPair.getPrivate(); // 配置Micronaut使用本地公钥验证JWT environment.put("micronaut.security.jwt.signatures.jwt.rsa.public-key", publicKey); } @Test void testLocalJwtValidation() { // 用私钥生成测试JWT String jwt = Jwts.builder() .subject("test-user") .signWith(privateKey, SignatureAlgorithm.RS256) .compact(); // 携带JWT调用接口验证 } }
方案2:使用Micronaut测试注解模拟认证
如果你的测试不需要验证JWT签名的合法性,只是需要模拟已认证的用户状态,可以直接用@JwtAuthentication注解跳过签名验证:
import io.micronaut.http.HttpResponse; import io.micronaut.http.HttpStatus; import io.micronaut.http.client.HttpClient; import io.micronaut.test.annotation.JwtAuthentication; import io.micronaut.test.extensions.junit5.annotation.MicronautTest; import jakarta.inject.Inject; import org.junit.jupiter.api.Test; import static org.junit.jupiter.api.Assertions.assertEquals; @MicronautTest class MockAuthTest { @Inject HttpClient client; @Test @JwtAuthentication(subject = "test-user", roles = {"admin"}) void testProtectedEndpoint() { HttpResponse<String> response = client.toBlocking() .exchange("/admin/endpoint", String.class); assertEquals(HttpStatus.OK, response.status()); } }
这个方案最简洁,但仅适用于不需要实际验证JWT签名的场景。
内容的提问来源于stack exchange,提问作者dcalap
相关产品推荐
相关产品推荐

