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

Spring Boot应用通过代理调用Azure Entra获取OAuth2令牌失败

解决Spring Boot调用Azure Entra OAuth2 API的代理配置问题

核心问题分析

你遇到的Connection refused错误,本质是Spring Security用于获取OAuth2令牌的内部WebClient未配置公司代理,无法访问Azure Entra的令牌端点。默认情况下,Spring OAuth2客户端不会自动继承应用级代理配置,必须显式自定义令牌获取专用的WebClient。

具体解决方案

1. 配置代理参数

先在application.yml中定义代理信息(如果代理无需认证,可去掉username和password字段):

proxy:
  host: 你的代理主机地址
  port: 你的代理端口
  username: 代理认证用户名
  password: 代理认证密码

spring:
  security:
    oauth2:
      client:
        registration:
          azure-entra: # 该ID需与后续代码中的clientRegistrationId一致
            client-id: 你的Azure客户端ID
            client-secret: 你的Azure客户端密钥
            authorization-grant-type: client_credentials # 根据实际授权类型调整,比如authorization_code
            scope: api://你的API范围/.default
        provider:
          azure-entra:
            issuer-uri: https://login.microsoftonline.com/你的租户ID/v2.0

2. 定义代理配置类

创建配置类绑定上述代理参数:

import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;

@Component
@ConfigurationProperties(prefix = "proxy")
public class ProxyProperties {
    private String host;
    private int port;
    private String username;
    private String password;

    // Getter和Setter方法
    public String getHost() { return host; }
    public void setHost(String host) { this.host = host; }
    public int getPort() { return port; }
    public void setPort(int port) { this.port = port; }
    public String getUsername() { return username; }
    public void setUsername(String username) { this.username = username; }
    public String getPassword() { return password; }
    public void setPassword(String password) { this.password = password; }
}

3. 自定义令牌获取用的WebClient及授权管理器

修改你的SecurityConfig,注入代理配置并构建带代理的WebClient:

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.client.reactive.ReactorClientHttpConnector;
import org.springframework.security.oauth2.client.AuthorizedClientServiceOAuth2AuthorizedClientManager;
import org.springframework.security.oauth2.client.OAuth2AuthorizedClientManager;
import org.springframework.security.oauth2.client.OAuth2AuthorizedClientProvider;
import org.springframework.security.oauth2.client.OAuth2AuthorizedClientProviderBuilder;
import org.springframework.security.oauth2.client.OAuth2AuthorizedClientService;
import org.springframework.security.oauth2.client.registration.ClientRegistrationRepository;
import org.springframework.security.oauth2.client.web.reactive.function.client.ServletOAuth2AuthorizedClientExchangeFilterFunction;
import org.springframework.web.reactive.function.client.WebClient;
import reactor.netty.http.client.HttpClient;
import reactor.netty.transport.ProxyProvider;

@Configuration
public class SecurityConfig {

    private final ProxyProperties proxyProperties;

    public SecurityConfig(ProxyProperties proxyProperties) {
        this.proxyProperties = proxyProperties;
    }

    // 构建带代理的HttpClient
    private HttpClient createProxyHttpClient() {
        ProxyProvider.Builder proxyBuilder = ProxyProvider.builder()
                .type(ProxyProvider.Proxy.HTTP)
                .host(proxyProperties.getHost())
                .port(proxyProperties.getPort());

        // 如果代理需要认证,添加用户名密码
        if (proxyProperties.getUsername() != null && !proxyProperties.getUsername().isEmpty()) {
            proxyBuilder.username(proxyProperties.getUsername())
                    .password(s -> proxyProperties.getPassword());
        }

        return HttpClient.create().tcpConfiguration(tcpClient -> tcpClient.proxy(proxyBuilder));
    }

    // 自定义用于获取令牌的WebClient
    @Bean
    public WebClient oauth2TokenWebClient() {
        return WebClient.builder()
                .clientConnector(new ReactorClientHttpConnector(createProxyHttpClient()))
                .build();
    }

    // 配置OAuth2授权管理器,指定用自定义WebClient获取令牌
    @Bean
    public OAuth2AuthorizedClientManager authorizedClientManager(
            ClientRegistrationRepository clientRegistrationRepository,
            OAuth2AuthorizedClientService authorizedClientService) {

        OAuth2AuthorizedClientProvider authorizedClientProvider = OAuth2AuthorizedClientProviderBuilder.builder()
                .clientCredentials() // 匹配你的授权类型,比如authorization_code
                .build();

        AuthorizedClientServiceOAuth2AuthorizedClientManager authorizedClientManager =
                new AuthorizedClientServiceOAuth2AuthorizedClientManager(
                        clientRegistrationRepository, authorizedClientService);
        authorizedClientManager.setAuthorizedClientProvider(authorizedClientProvider);

        // 注入自定义WebClient到令牌获取上下文
        authorizedClientManager.setContextAttributesMapper(contextAttributes -> {
            contextAttributes.put(ServletOAuth2AuthorizedClientExchangeFilterFunction.DEFAULT_WEB_CLIENT_ATTRIBUTE_NAME,
                    oauth2TokenWebClient());
            return contextAttributes;
        });

        return authorizedClientManager;
    }

    // 业务调用API用的WebClient,同样配置代理并集成OAuth2认证
    @Bean
    public WebClient apiWebClient(OAuth2AuthorizedClientManager authorizedClientManager) {
        ServletOAuth2AuthorizedClientExchangeFilterFunction oauth2Filter =
                new ServletOAuth2AuthorizedClientExchangeFilterFunction(authorizedClientManager);
        oauth2Filter.setDefaultClientRegistrationId("azure-entra"); // 对应配置文件中的client registration ID

        return WebClient.builder()
                .clientConnector(new ReactorClientHttpConnector(createProxyHttpClient()))
                .filter(oauth2Filter)
                .build();
    }
}

关键注意事项

  • 确保代码中的clientRegistrationId与application.yml里的spring.security.oauth2.client.registration下的ID完全一致。
  • 根据实际使用的OAuth2授权类型(如authorization_code、password等),调整OAuth2AuthorizedClientProviderBuilder的配置项。
  • 如果代理无需认证,直接删除代码中用户名密码相关的逻辑即可。

内容的提问来源于stack exchange,提问作者Wilberforce

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.12 22:32:46