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

使用托管标识连接Azure SQL遇令牌过期登录失败的解决方法问询

问题原因

该报错的根本原因是Azure AD颁发的Azure SQL访问令牌默认有效期仅为1小时,你的代码是在SqlConnection实例创建时一次性获取令牌并赋值,长耗时场景下,后续需要打开连接执行操作时令牌已经过期,就会抛出登录失败错误。

推荐解决方案

方案1:使用SqlClient内置托管标识认证(最优)

Microsoft.Data.SqlClient 2.1.0及以上版本已经原生支持托管标识自动认证和令牌刷新,不需要手动获取、赋值AccessToken,操作如下:

  • 修改Azure Key Vault中存储的SQL连接字符串,新增Authentication=Active Directory Default参数,示例:
    Server=tcp:<你的SQL实例名>.database.windows.net,1433;Initial Catalog=<库名>;Authentication=Active Directory Default;Encrypt=True;TrustServerCertificate=False;Connection Timeout=30;
  • 简化数据库连接创建逻辑,无需再手动处理令牌:
public SqlConnection GetDBConnection()
{
    SqlConnection conn = new SqlConnection();
    conn.ConnectionString = Environment.GetEnvironmentVariable("SqlConnectionString");
    // 不需要手动赋值AccessToken,SqlClient会自动处理令牌获取、刷新逻辑
    return conn;
}

该方案底层会自动调用DefaultAzureCredential获取令牌,并且内置了令牌过期刷新逻辑,完全不需要自己维护令牌生命周期,不会再出现间隔后操作过期的问题。

方案2:使用EF Core连接拦截器动态赋值令牌

如果你使用的SqlClient版本过低,或者有自定义令牌获取逻辑的需求,可以通过EF Core的连接拦截器,在每次数据库连接实际打开前动态获取最新令牌赋值,避免一次性赋值过期问题:

步骤1:实现自定义连接拦截器

public class AzureSqlAccessTokenInterceptor : DbConnectionInterceptor
{
    private readonly DefaultAzureCredential _credential = new DefaultAzureCredential();
    private readonly TokenRequestContext _tokenContext = new TokenRequestContext(new[] { "https://database.windows.net/.default" });

    public override InterceptionResult ConnectionOpening(DbConnection connection, ConnectionEventData eventData, InterceptionResult result)
    {
        if (connection is SqlConnection sqlConn)
        {
            // 每次连接打开前获取最新令牌,DefaultAzureCredential会自动缓存未过期的令牌,不会频繁请求Azure AD
            var accessToken = _credential.GetToken(_tokenContext, CancellationToken.None);
            sqlConn.AccessToken = accessToken.Token;
        }
        return base.ConnectionOpening(connection, eventData, result);
    }

    public override async ValueTask<InterceptionResult> ConnectionOpeningAsync(DbConnection connection, ConnectionEventData eventData, InterceptionResult result, CancellationToken cancellationToken = default)
    {
        if (connection is SqlConnection sqlConn)
        {
            var accessToken = await _credential.GetTokenAsync(_tokenContext, cancellationToken);
            sqlConn.AccessToken = accessToken.Token;
        }
        return await base.ConnectionOpeningAsync(connection, eventData, result, cancellationToken);
    }
}

步骤2:注册拦截器到DbContext

在你的DbContext配置处添加拦截器注册:

services.AddDbContext<你的DbContext类名>(options =>
{
    options.UseSqlServer(Environment.GetEnvironmentVariable("SqlConnectionString"))
           .AddInterceptors(new AzureSqlAccessTokenInterceptor());
});
额外注意事项
  • 不要在长耗时流程中长时间持有已打开的数据库连接,尽量按需打开连接,连接池会自动管理连接复用
  • DefaultAzureCredential内置了令牌缓存逻辑,只有当令牌接近过期时才会重新向Azure AD请求新令牌,不会有性能损耗

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.10.02 15:45:03