如何通过AAD/MFA凭据连接多Azure SQL数据库并避免重复认证
解决Azure SQL同实例多数据库MFA单次认证问题
你遇到的问题是因为每次创建新的SqlConnection时,交互式认证流程都会重新触发,没有复用之前获取的令牌。好在我们可以通过复用TokenCache来实现同一实例下多个数据库只需要一次浏览器认证,下面是具体的解决方案和代码修改:
核心思路
Azure SQL的交互式认证依赖于Azure AD的访问令牌,同一SQL实例下的所有数据库共享同一个资源URI(https://database.windows.net/),所以只要我们缓存已获取的有效令牌,后续连接就可以直接复用,无需重新弹出登录窗口。
修改后的代码实现
1. 改造认证提供者,添加全局令牌缓存
我们需要在ActiveDirectoryAuthProvider中添加一个静态的TokenCache实例,确保所有认证请求复用同一个缓存:
using Microsoft.IdentityModel.Clients.ActiveDirectory; using System.Data.SqlClient; using System.Linq; public class ActiveDirectoryAuthProvider : SqlAuthenticationProvider { private readonly string _clientId = "MyClientID"; // 全局静态TokenCache,所有认证请求共享 private static readonly TokenCache _globalTokenCache = new TokenCache(); public override async Task<SqlAuthenticationToken> AcquireTokenAsync(SqlAuthenticationParameters parameters) { // 使用全局缓存初始化认证上下文 var authContext = new AuthenticationContext(parameters.Authority, _globalTokenCache); authContext.CorrelationId = parameters.ConnectionId; // 先检查缓存中是否有未过期的有效令牌 var validCachedToken = _globalTokenCache.ReadItems() .FirstOrDefault(t => t.Resource == parameters.Resource && t.ExpiresOn > DateTimeOffset.Now); if (validCachedToken != null) { Console.WriteLine("复用缓存中的有效令牌"); return new SqlAuthenticationToken(validCachedToken.AccessToken, validCachedToken.ExpiresOn); } AuthenticationResult result; switch (parameters.AuthenticationMethod) { case SqlAuthenticationMethod.ActiveDirectoryInteractive: Console.WriteLine("触发交互式认证获取新令牌"); // 控制台应用使用urn:ietf:wg:oauth:2.0:oob作为重定向URI result = await authContext.AcquireTokenAsync( parameters.Resource, _clientId, new Uri("urn:ietf:wg:oauth:2.0:oob"), new PlatformParameters(PromptBehavior.Auto)); // PromptBehavior.Auto会自动判断是否需要弹出登录窗口,缓存有效时不会弹出 break; case SqlAuthenticationMethod.ActiveDirectoryIntegrated: Console.WriteLine("使用集成认证获取新令牌"); result = await authContext.AcquireTokenAsync( parameters.Resource, _clientId, new UserCredential(GlobalSettings.CredentialsSettings.Username)); break; default: throw new InvalidOperationException("不支持当前认证方法"); } return new SqlAuthenticationToken(result.AccessToken, result.ExpiresOn); } public override bool IsSupported(SqlAuthenticationMethod authenticationMethod) { return authenticationMethod == SqlAuthenticationMethod.ActiveDirectoryIntegrated || authenticationMethod == SqlAuthenticationMethod.ActiveDirectoryInteractive; } }
2. 确保认证提供者只初始化一次
在Main方法中,只需要设置一次认证提供者,不要每次连接都重复设置:
static void Main(string[] args) { // 仅初始化一次认证提供者 var provider = new ActiveDirectoryAuthProvider(); SqlAuthenticationProvider.SetProvider( SqlAuthenticationMethod.ActiveDirectoryInteractive, provider); SqlAuthenticationProvider.SetProvider( SqlAuthenticationMethod.ActiveDirectoryIntegrated, provider); // 后续可以多次调用GetConnection连接不同数据库 using var db1Conn = GetConnection("Database1"); // 执行数据库1的操作... using var db2Conn = GetConnection("Database2"); // 执行数据库2的操作... } // 修改GetConnection方法,支持传入数据库名称 private SqlConnection GetConnection(string databaseName) { var builder = new SqlConnectionStringBuilder(); builder.DataSource = "MyServer.database.windows.net"; builder.Encrypt = true; builder.TrustServerCertificate = false; // 建议关闭,使用Azure SQL的合法证书 builder.PersistSecurityInfo = true; builder.Authentication = SqlAuthenticationMethod.ActiveDirectoryInteractive; builder.InitialCatalog = databaseName; var conn = new SqlConnection(builder.ToString()); conn.Open(); return conn; }
关键说明
- TokenCache复用:静态的
_globalTokenCache会在应用生命周期内保存所有获取的令牌,同一资源URI的请求会优先使用缓存中的有效令牌。 - PromptBehavior.Auto:这个参数会让Azure AD自动判断是否需要弹出登录窗口,如果缓存中有有效令牌则直接复用,不会弹出浏览器。
- 同一实例资源共享:同一Azure SQL实例下的所有数据库都使用
https://database.windows.net/作为资源URI,所以缓存的令牌完全可以跨数据库复用。
这样修改后,你第一次连接数据库时会弹出浏览器登录,后续连接同一实例下的其他数据库时,就会自动复用缓存的令牌,不再弹出登录窗口了。
内容的提问来源于stack exchange,提问作者Obsidian Phoenix




