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

Azure WebJob EF Core托管身份连SQL 令牌过期报错问题

问题场景

我在Azure平台部署了配置为连续运行模式的WebJob,作业内部通过Entity Framework Core实现数据库操作,同时使用用户分配托管标识(user-assigned Managed Identity)获取访问令牌,将令牌赋值给数据库连接的AccessToken属性完成身份认证。已知用户分配托管标识的令牌默认有效期为24小时,WebJob首次启动后的第一次执行可以正常跑完所有逻辑,但运行时长超过24小时之后的后续作业迭代全部执行失败,触发报错:Login failed for user '<token-identified principal>'. Token is expired,即令牌过期导致用户登录失败。

现有实现代码

Program.cs

public static void Main()
{
    var builder = new ConfigurationBuilder()
        .SetBasePath(Directory.GetCurrentDirectory())
        .AddJsonFile("appsettings.json", optional: false, reloadOnChange: true)
        .AddEnvironmentVariables();
    IConfiguration configuration = builder.Build();
    IHost host = new HostBuilder()
        .ConfigureWebJobs(webJobConfiguration =>
        {
            webJobConfiguration.AddAzureStorage();
            webJobConfiguration.AddAzureStorageCoreServices();
            webJobConfiguration.AddTimers();
        })
        .ConfigureServices(serviceCollection =>
        {
            serviceCollection.AddTransient<ImportFunctions>();
            serviceCollection.AddSingleton<IConfiguration>(configuration);
            serviceCollection.AddSingleton(new ConfigManager(configuration));
            serviceCollection.AddTransient(typeof(IDBAuthTokenService), typeof(AzureSqlAuthTokenService));
            serviceCollection.AddDbContext<AppDbContext>(options => options.UseSqlServer(GetConnectionString()));
        })
        .Build();
    using (host)
    {
        host.Run();
    }

    string GetConnectionString()
    {
        string connection = configuration["connectionString"];
        bool readFromKeyVault = bool.Parse(configuration["ReadFromKeyVault"] ?? "false");
        if (readFromKeyVault)
        {
            connection = GetKeyVaultClient().GetSecretValue("appconnectionstring");
        }
        return connection;
    }

    KeyVaultAccessClient GetKeyVaultClient()
    {
        string keyVaultURL = configuration["KeyVaultURL"];
        string userAssignedClientId = configuration["MsiConfiguration:UserAssignedClientId"];
        return new KeyVaultAccessClient(keyVaultURL, userAssignedClientId);
    }
}

IDBAuthTokenService.cs

public interface IDBAuthTokenService
{
    Task<string> GetTokenAsync();
}

AzureSqlAuthTokenService.cs

public class AzureSqlAuthTokenService : IDBAuthTokenService
{
    public AzureSqlAuthTokenService()
    {
    }

    public async Task<string> GetTokenAsync()
    {
        var credential = new DefaultAzureCredential(new DefaultAzureCredentialOptions
        {
            ManagedIdentityClientId = ConfigManager.Get("UserAssignedClientId")
        });
        var tokenRequestContext = new TokenRequestContext(new[] { ConfigManager.Get("AzureSQLResourceId") });
        var token = await credential.GetTokenAsync(tokenRequestContext, default);
        return token.Token;
    }
}

AppDbContext.cs

public partial class AppDbContext : DbContext
{
    public AppDbContext()
    {
    }

    public AppDbContext(IDBAuthTokenService tokenService, DbContextOptions<AppDbContext> options) : base(options)
    {
        var connection = this.Database.GetDbConnection() as SqlConnection;
        connection.AccessToken = tokenService.GetTokenAsync().Result;
    }

    public AppDbContext(DbContextOptions<AppDbContext> options) : base(options)
    {
    }
}
已尝试的修复方案

为解决令牌过期问题,我引入Polly NuGet包实现异常重试逻辑,相关代码如下:

ImportFunctions.cs

public class ImportFunctions
{
    private IEmailEID _emailEID;
    private IConfiguration _config;
    private readonly IDBAuthTokenService _tokenService;
    private AsyncRetryPolicy retryPolicy;

    public ImportFunctions(IEmailEID emailEID, IConfiguration config, IDBAuthTokenService tokenService)
    {
        _emailEID = emailEID;
        _config = config;
        _tokenService = tokenService;
        #region 重试配置
        int MAX_RETRIES = 3;
        retryPolicy = Policy.Handle<Exception>(ex => ex.Message.Trim().Length > 0)
            .WaitAndRetryAsync(
                retryCount: MAX_RETRIES,
                sleepDurationProvider: (attemptCount) => TimeSpan.FromSeconds(attemptCount * 2),
                onRetry: (exception, sleepDuration, attemptNumber, context) =>
                {
                    Console.WriteLine($"异常详情: {exception.Message}");
                    ReInitializeAppDbContext();
                });
        #endregion
    }

    public void DailyTrigger([TimerTrigger(typeof(DailyJobSchedule))] TimerInfo timerInfo)
    {
        try
        {
            if (!bool.Parse(_config["disableEmails"]))
            {
                // 此处为每日定时执行的业务逻辑
                Console.WriteLine("每日任务触发时间 :" + DateTime.Now);
                _emailEID.TestMethod1();
                _emailEID.TestMethod2();
                Console.WriteLine("每日任务结束时间 :" + DateTime.Now);
            }
        }
        catch (Exception ex)
        {
            Console.WriteLine("异常信息 :" + ex.Message);
        }
    }

    private void ReInitializeAppDbContext()
    {
        var contextOptions = new DbContextOptionsBuilder<AppDbContext>()
            .UseSqlServer(GetConnectionString())
            .Options;
        using var context = new AppDbContext(_tokenService, contextOptions);
    }
}

我的预期是:24小时后令牌过期触发数据库访问异常时,重试逻辑会调用ReInitializeAppDbContext()方法,使用新获取的有效令牌重新初始化DbContext,后续数据库调用即可正常执行。
但实际运行中,WebJob运行时长超过24小时后的所有执行仍然抛出令牌过期导致的登录失败错误,现有修复逻辑未生效,请问该如何定位并解决该问题?


内容的提问来源于stack exchange,提问作者santosh kumar patro

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.29 21:36:26