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

IConfidentialClientApplication的AcquireTokenForClient周期性抛出异常求助

问题描述

我构建了一个NotificationService工厂用于检查两个不同的邮箱,整体运行正常,但会周期性收到错误邮件。以下是抛出异常的方法及异常信息:

抛出异常的方法

private async Task<string> GetAccessToken()
{
    try { 
        string token = string.Empty;
        IConfidentialClientApplication app = ConfidentialClientApplicationBuilder.Create(_settings.AzureClientID)
        .WithCertificate(_settings.Certificate)
        .WithAuthority($"https://login.microsoftonline.com/{_settings.AzureTenantID}")
        .WithRedirectUri("https://daemon")
        .Build();

        string[] scopes = new string[] { "https://graph.microsoft.com/.default" };

        var result = await app.AcquireTokenForClient(scopes).ExecuteAsync();

        return result.AccessToken;
    }
    catch (Exception ex)
    {
        _logger.LogError($"{DateTime.Now.ToString()}: Exception within GetAccessToken\r\n{ex}", ex);
    }
    return string.Empty;
}

异常信息

2023年2月8日 上午9:28:19:GetAccessToken内发生异常
System.Net.Http.HttpRequestException: 发送请求时发生错误。
---> System.IO.IOException: 无法从传输连接读取数据:连接尝试失败,因为连接方在一段时间后未正确响应,或者已建立的连接失败,因为连接的主机未能响应。
---> System.Net.Sockets.SocketException (10060): 连接尝试失败,因为连接方在一段时间后未正确响应,或者已建立的连接失败,因为连接的主机未能响应。
--- 内部异常堆栈跟踪结束 ---
at System.Net.Sockets.Socket.AwaitableSocketAsyncEventArgs.ThrowException(SocketError error, CancellationToken cancellationToken)
at System.Net.Sockets.Socket.AwaitableSocketAsyncEventArgs.GetResult(Int16 token)
at System.Net.Security.SslStream.ReadAsyncInternal[TIOAdapter](TIOAdapter adapter, Memory1 buffer) at System.Net.Http.HttpConnection.SendAsyncCore(HttpRequestMessage request, Boolean async, CancellationToken cancellationToken) --- 内部异常堆栈跟踪结束 --- at System.Net.Http.HttpConnection.SendAsyncCore(HttpRequestMessage request, Boolean async, CancellationToken cancellationToken) at System.Net.Http.AuthenticationHelper.SendWithNtAuthAsync(HttpRequestMessage request, Uri authUri, Boolean async, ICredentials credentials, Boolean isProxyAuth, HttpConnection connection, HttpConnectionPool connectionPool, CancellationToken cancellationToken) at System.Net.Http.HttpConnectionPool.SendWithRetryAsync(HttpRequestMessage request, Boolean async, Boolean doRequestAuth, CancellationToken cancellationToken) at System.Net.Http.AuthenticationHelper.SendWithAuthAsync(HttpRequestMessage request, Uri authUri, Boolean async, ICredentials credentials, Boolean preAuthenticate, Boolean isProxyAuth, Boolean doRequestAuth, HttpConnectionPool pool, CancellationToken cancellationToken) at System.Net.Http.RedirectHandler.SendAsync(HttpRequestMessage request, Boolean async, CancellationToken cancellationToken) at System.Net.Http.HttpClient.SendAsyncCore(HttpRequestMessage request, HttpCompletionOption completionOption, Boolean async, Boolean emitTelemetryStartStop, CancellationToken cancellationToken) at Microsoft.Identity.Client.Http.HttpManager.ExecuteAsync(Uri endpoint, IDictionary2 headers, HttpContent body, HttpMethod method, ILoggerAdapter logger, CancellationToken cancellationToken)
at Microsoft.Identity.Client.Http.HttpManager.ExecuteWithRetryAsync(Uri endpoint, IDictionary2 headers, HttpContent body, HttpMethod method, ILoggerAdapter logger, Boolean doNotThrow, Boolean retry, CancellationToken cancellationToken) at Microsoft.Identity.Client.Http.HttpManager.SendPostAsync(Uri endpoint, IDictionary2 headers, HttpContent body, ILoggerAdapter logger, CancellationToken cancellationToken)
at Microsoft.Identity.Client.Http.HttpManager.SendPostAsync(Uri endpoint, IDictionary2 headers, IDictionary2 bodyParameters, ILoggerAdapter logger, CancellationToken cancellationToken)
at Microsoft.Identity.Client.OAuth2.OAuth2Client.ExecuteRequestAsync[T](Uri endPoint, HttpMethod method, RequestContext requestContext, Boolean expectErrorsOn200OK, Boolean addCommonHeaders, Func2 onBeforePostRequestData) at Microsoft.Identity.Client.OAuth2.OAuth2Client.GetTokenAsync(Uri endPoint, RequestContext requestContext, Boolean addCommonHeaders, Func2 onBeforePostRequestHandler)
at Microsoft.Identity.Client.OAuth2.TokenClient.SendHttpAndClearTelemetryAsync(String tokenEndpoint, ILoggerAdapter logger)
at Microsoft.Identity.Client.OAuth2.TokenClient.SendTokenRequestAsync(IDictionary`2 additionalBodyParameters, String scopeOverride, String tokenEndpointOverride, CancellationToken cancellationToken)
at Microsoft.Identity.Client.Internal.Requests.ClientCredentialRequest.FetchNewAccessTokenAsync(CancellationToken cancellationToken)
at Microsoft.Identity.Client.Internal.Requests.ClientCredentialRequest.ExecuteAsync(CancellationToken cancellationToken)
at Microsoft.Identity.Client.Internal.Requests.RequestBase.RunAsync(CancellationToken cancellationToken)
at Microsoft.Identity.Client.ApiConfig.Executors.ConfidentialClientExecutor.ExecuteAsync(AcquireTokenCommonParameters commonParameters, AcquireTokenForClientParameters clientParameters, CancellationToken cancellationToken)
at HIW.NotificationService.Service.GraphService.GetAccessToken() in C:\agent_work\60\s\HIW.NotificationService\Service\GraphService.cs:line 276

问题分析与解决建议

这个错误是Socket连接超时(错误码10060),本质是程序向Azure AD的token端点发起请求时,网络层面无法建立连接或连接超时,属于间歇性网络问题。可以从以下几个方向修复:

  • 复用MSAL应用实例:当前代码每次调用GetAccessToken都新建IConfidentialClientApplication实例,低效且易引发网络连接问题。应将实例设为类的私有成员,在构造函数中初始化一次,复用它。
  • 启用令牌缓存:MSAL内置令牌缓存机制,复用实例后会自动缓存令牌,减少不必要的请求,降低触发网络问题的概率。
  • 添加重试策略:给令牌请求添加重试逻辑,MSAL支持通过.WithRetry()配置重试次数,也可以用Polly框架实现更灵活的瞬时故障重试。
  • 检查网络环境:确认服务器到login.microsoftonline.com的443端口连通性,排查是否有防火墙、代理偶尔拦截请求,或网络带宽波动导致超时。
  • 设置请求超时:给MSAL的HTTP请求配置合理的超时时间,避免无限制等待。

修改后的代码示例

// 类私有成员,复用MSAL应用实例
private readonly IConfidentialClientApplication _msalApp;
private readonly YourSettingsType _settings;

// 构造函数中初始化应用实例
public GraphService(YourSettingsType settings)
{
    _settings = settings;
    _msalApp = ConfidentialClientApplicationBuilder.Create(_settings.AzureClientID)
        .WithCertificate(_settings.Certificate)
        .WithAuthority($"https://login.microsoftonline.com/{_settings.AzureTenantID}")
        .WithRedirectUri("https://daemon")
        .Build();
}

private async Task<string> GetAccessToken()
{
    try { 
        string[] scopes = new string[] { "https://graph.microsoft.com/.default" };
        // 优先从缓存获取令牌,缓存失效时自动请求新令牌,并配置重试
        var result = await _msalApp.AcquireTokenForClient(scopes)
            .WithRetry(3) // 配置3次重试
            .ExecuteAsync();

        return result.AccessToken;
    }
    catch (Exception ex)
    {
        _logger.LogError($"{DateTime.Now.ToString()}: Exception within GetAccessToken\r\n{ex}", ex);
    }
    return string.Empty;
}

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.02 02:00:38