使用托管标识连接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
相关产品推荐
相关产品推荐

