Spring Boot OAuth2 Client对接Azure AD B2C时Issuer URI不匹配问题
解决Spring Boot OAuth2 Client对接Azure AD B2C的Issuer不匹配问题
问题说明
我搭建了Spring Boot OAuth2 Client对接Azure AD B2C,配置如下:
spring: security: oauth2: client: registration: azuread: authorization-grant-type: authorization_code client-id: 'client id' client-secret: 'secret' provider: azuread scope: openid client-name: demo user-flows: sign-up-or-sign-in: <policy name> provider: azuread: issuer-uri: https://{clientId}.b2clogin.com/{clientId}.onmicrosoft.com/<policy-name>/v2.0/.well-known/openid-configuration
启动应用时触发报错:
"The Issuer provided in the configuration metadata did not match the requested issuer"
排查后发现,Spring根据配置的issuer-uri拉取元数据后,校验Issuer出现不一致:
预期值:
https://{clientId}.b2clogin.com/{clientId}.onmicrosoft.com//v2.0/实际结果:
https://{clientId}.b2clogin.com/{clientId}.onmicrosoft.com/v2.0/
要求:不使用Spring Boot Azure AD B2C Starter,仅通过原生OAuth2 Client解决该问题。
解决方案
方法1:直接调整配置文件,跳过元数据自动发现
手动指定所有OAuth2端点,同时把issuer-uri设置为实际返回的正确值,避免自动发现时的路径解析问题:
spring: security: oauth2: client: registration: azuread: authorization-grant-type: authorization_code client-id: 'client id' client-secret: 'secret' provider: azuread scope: openid client-name: demo # 手动配置授权、令牌、JWKS端点 authorization-uri: https://{clientId}.b2clogin.com/{clientId}.onmicrosoft.com/<policy-name>/oauth2/v2.0/authorize token-uri: https://{clientId}.b2clogin.com/{clientId}.onmicrosoft.com/<policy-name>/oauth2/v2.0/token jwk-set-uri: https://{clientId}.b2clogin.com/{clientId}.onmicrosoft.com/<policy-name>/discovery/v2.0/keys provider: azuread: # 设置为实际返回的Issuer值 issuer-uri: https://{clientId}.b2clogin.com/{clientId}.onmicrosoft.com/v2.0/
这种方式直接绕开元数据自动发现的路径解析问题,确保Issuer校验完全匹配。
方法2:自定义Issuer校验逻辑
如果需要保留元数据自动发现,可以自定义JWT校验器,忽略Issuer中的斜杠差异:
import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.security.oauth2.client.registration.ClientRegistrationRepository; import org.springframework.security.oauth2.jwt.Jwt; import org.springframework.security.oauth2.jwt.JwtDecoder; import org.springframework.security.oauth2.jwt.JwtDecoders; import org.springframework.security.oauth2.jwt.JwtValidator; import org.springframework.security.oauth2.jwt.NimbusJwtDecoder; @Configuration public class OAuth2IssuerConfig { @Bean public JwtDecoder jwtDecoder(ClientRegistrationRepository clientRegistrationRepository) { var registration = clientRegistrationRepository.findByRegistrationId("azuread"); NimbusJwtDecoder decoder = JwtDecoders.fromIssuerLocation(registration.getIssuerUri()); // 自定义校验器,处理斜杠差异 JwtValidator validator = jwt -> { String expectedIssuer = registration.getIssuerUri(); String actualIssuer = jwt.getIssuer().toString(); // 去掉所有重复斜杠后对比 if (!expectedIssuer.replaceAll("//", "/").equals(actualIssuer.replaceAll("//", "/"))) { throw new IllegalArgumentException("Issuer mismatch"); } return jwt; }; decoder.setJwtValidator(validator); return decoder; } }
这个配置会覆盖默认的Issuer校验逻辑,允许预期值和实际值之间的斜杠差异。
方法3:自定义元数据解析逻辑
通过修改元数据解析过程,调整返回的Issuer值,使其和配置中的预期值匹配:
import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.security.oauth2.client.registration.ClientRegistration; import org.springframework.security.oauth2.client.registration.ClientRegistrationRepository; import org.springframework.security.oauth2.client.registration.InMemoryClientRegistrationRepository; import org.springframework.security.oauth2.client.endpoint.DefaultAuthorizationCodeTokenResponseClient; import org.springframework.security.oauth2.core.endpoint.OAuth2TokenResponse; import java.util.Map; @Configuration public class AzureB2CMetadataConfig { @Bean public ClientRegistrationRepository clientRegistrationRepository() { ClientRegistration registration = ClientRegistration.withRegistrationId("azuread") .clientId("your-client-id") .clientSecret("your-client-secret") .authorizationGrantType(org.springframework.security.oauth2.core.AuthorizationGrantType.AUTHORIZATION_CODE) .scope("openid") .authorizationUri("https://{clientId}.b2clogin.com/{clientId}.onmicrosoft.com/<policy-name>/oauth2/v2.0/authorize") .tokenUri("https://{clientId}.b2clogin.com/{clientId}.onmicrosoft.com/<policy-name>/oauth2/v2.0/token") .jwkSetUri("https://{clientId}.b2clogin.com/{clientId}.onmicrosoft.com/<policy-name>/discovery/v2.0/keys") .issuerUri("https://{clientId}.b2clogin.com/{clientId}.onmicrosoft.com//v2.0/") // 配置预期的带双斜杠的Issuer .clientName("demo") .build(); return new InMemoryClientRegistrationRepository(registration); } @Bean public DefaultAuthorizationCodeTokenResponseClient tokenResponseClient() { DefaultAuthorizationCodeTokenResponseClient client = new DefaultAuthorizationCodeTokenResponseClient(); // 修改响应解析逻辑,把实际Issuer改成带双斜杠的预期值 client.setTokenResponseConverter(response -> { Map<String, Object> body = response.getBody(); String actualIssuer = (String) body.get("iss"); // 替换成预期的带双斜杠的格式 body.put("iss", actualIssuer.replace("/v2.0/", "//v2.0/")); return OAuth2TokenResponse.withResponseParameters(body) .tokenType(body.get("token_type").toString()) .accessToken(body.get("access_token").toString()) .expiresIn(Long.parseLong(body.get("expires_in").toString())) .idToken(body.get("id_token").toString()) .build(); }); return client; } }
内容的提问来源于stack exchange,提问作者user1593966
相关产品推荐
相关产品推荐

