用户"令牌标识的主体><登录失败。令牌已过期



我在Azure中托管了一个webjob,计划运行连续模式类型。在这个webjob中,我有一些使用EntityFrameworkCore管理的数据库操作。我还使用userassigned ManagedIdentity来获取令牌并将其传递给连接的AccessToken属性。用户分配的managediidentity的令牌生存期为24小时。webjob第一次成功运行,没有任何问题,但在24小时后的后续迭代中,它会中断,因为那时令牌已经过期。

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 RetryDetails
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 details: {exception.Message}");
ReInitializeAppDbContext();
});
#endregion
}
public void DailyTrigger([TimerTrigger(typeof(DailyJobSchedule))] TimerInfo timerInfo)
{
try
{
if (!bool.Parse(_config["disableEmails"]))
{
//Code here runs every day                 
Console.WriteLine("Daily Job triggered at :" + DateTime.Now);
_emailEID.TestMethod1();
_emailEID.TestMethod2();
Console.WriteLine("Daily Job ended at :" + DateTime.Now);
}
}
catch (Exception ex)
{
Console.WriteLine("Exception :" + ex.Message);
}
}
private void ReInitializeAppDbContext()
{
var contextOptions = new DbContextOptionsBuilder<AppDbContext>().UseSqlServer(GetConnectionString()).Options;
using var context = new AppDbContext(_tokenService, contextOptions);
}
}

24小时后发生的任何数据库调用都失败,因为令牌已经过期,在这种情况下,我试图用方法中提到的新令牌重新初始化dbcontext: ReInitializeAppDbContext()

但是我仍然看到错误:Login failed for user "。令牌在24小时后的所有执行中过期。

谁能帮我解决这个问题?

我建议利用最新版本的Microsoft.Data.SqlClient NuGet包,它负责令牌获取/缓存/更新过程。

请看我写的这篇博文:https://mderriey.com/2021/07/23/new-easy-way-to-use-aad-auth-with-azure-sql/

如果你不能使用它或想要自己获得令牌,我建议在代码中添加日志记录,以确保令牌更新过程确实按照你的期望发生。

在新版本的Microsoft.Data.SqlClient出现之前,我成功地使用了以下博文中的代码:https://mderriey.com/2020/09/12/resolve-ef-core-interceptors-with-dependency-injection/

相关内容

  • 没有找到相关文章