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
相关产品推荐
相关产品推荐

