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

MS Dynamics首次身份验证令牌过期后抛出对象已释放错误

问题触发原因

  • ISecureClientSecret对象被提前释放:你怀疑的方向是对的,绝大多数ISecureClientSecret的官方实现都继承了IDisposable接口,如果你初始化时传入的_secret是局部创建对象、或者在依赖注入容器中注册为非单例生命周期,上层逻辑或者容器会在首次使用完成后主动释放该对象。首次调用initClient时对象还未被释放,所以能正常拿到令牌,等到令牌过期再次调用时,静态变量clientSecret指向的对象已经被回收/释放,AcquireTokenAsync内部读取密钥内容时就会抛出对象已释放错误。
  • ADAL库的已知兼容性问题:你当前用的AuthenticationContext属于已经停止维护的ADAL(Azure Active Directory Authentication Library)库,旧版本的ADAL在同步调用异步方法、复用ClientCredential实例时,确实存在内部对象被提前回收的未公开bug,和你碰到的报错场景完全匹配。
  • 同步工具类的隐式副作用:你用的RunSync方法虽然是通用的异步转同步实现,但在某些.NET Framework版本下,会导致异步执行上下文被提前销毁,使得AcquireTokenAsync内部用到的上下文对象被标记为已释放。

对应解决方法

  • 优先修复密钥释放问题:不要直接存储传入的ISecureClientSecret引用,初始化时就把密钥内容读取出来存为静态SecureString变量,每次调用initClient生成新的ClientCredential时都用这个存储的密钥值重新创建,完全避免依赖外部传入的对象生命周期:
// 新增静态变量存储密钥内容
private static SecureString _cachedSecret;

internal static void Init(string _clientId, ISecureClientSecret _secret, string _authority = null, string _resourceUrl = null)
{
    clientId = _clientId;
    // 初始化时就把密钥内容缓存下来,不保留原_secret的引用
    _cachedSecret = new SecureString();
    // 此处调用ISecureClientSecret的实现方法取出密钥内容存入_cachedSecret,不同实现类取法可能有差异,以你用到的实现为准
    // 示例:如果是微软官方的SecureClientSecret,可以通过反射读取内部值,或者直接改用字符串密钥构造
    if (!string.IsNullOrWhiteSpace(_authority))
    {
        authority = _authority;
    }
    if (!string.IsNullOrWhiteSpace(_resourceUrl))
    {
        resourceUrl = _resourceUrl;
    }
    initClient();
}

private static void initClient() 
{
    try
    {
        // 每次都用缓存的密钥新建ClientCredential,不依赖原ISecureClientSecret对象
        var clientCredentials = new ClientCredential(clientId, _cachedSecret);
        var authContext = new AuthenticationContext(authority, false);
        var authResult = DynamicsLeadWorkflow.AsyncUtil.RunSync<AuthenticationResult>(() => authContext.AcquireTokenAsync(resourceUrl, clientCredentials));
        // 剩余原有逻辑不变
        if(authResult == null || String.IsNullOrWhiteSpace(authResult.AccessToken))
        {
            throw new UnauthorizedAccessException("Couldn't acquire the token from Dynamics");
        }
        // 建议HttpClient改成单例,不要每次都新建,避免端口占用问题
        _client ??= new HttpClient();
        _client.DefaultRequestHeaders.Authorization = new System.Net.Http.Headers.AuthenticationHeaderValue("Bearer", authResult.AccessToken);
        _client.BaseAddress = new Uri(resourceUrl);
        clientCreated = DateTime.Now;
    }
    catch (Exception ex)
    {
        ERROR(ex);
    }
}
  • 长期方案建议替换认证库:把已经废弃的ADAL库替换为微软当前主推的MSAL(Microsoft.Identity.Client)库,MSAL原生支持令牌自动刷新、静态实例复用,不需要自己手动判断令牌过期重新调用接口,完全规避当前碰到的问题。
  • 临时规避同步调用问题:如果暂时不想改库,可以把initClient改成异步方法,直接await AcquireTokenAsync的返回结果,去掉RunSync的包装,也能解决上下文销毁导致的释放报错。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.09.24 21:45:09