【问题标题】:Performance issue for Managed identity for SQL authentication via azure function App通过 azure function App 进行 SQL 身份验证的托管标识的性能问题
【发布时间】:2021-10-02 01:14:35
【问题描述】:

以下代码可以正常工作,但会导致性能问题,因为每个请求平均要多花 3 秒。 有什么方法可以解决这个问题?

public QuoteContext(DbContextOptions options) : base(options)
{
    var conn = (Microsoft.Data.SqlClient.SqlConnection)Database.GetDbConnection();
    var credential = new DefaultAzureCredential();
    var token = credential
            .GetToken(new Azure.Core.TokenRequestContext(
                new[] { "https://database.windows.net/.default" }));
    conn.AccessToken = token.Token;
}

【问题讨论】:

  • 这里的问题是DefaultAzureCredential 没有缓存令牌,因此它会为每个新连接调用 AAD。你可能想围绕它实现一些缓存。
  • 谢谢。我尝试通过将生成的令牌存储在静态变量中然后检查令牌的有效性(如果有效)然后从静态变量中选择,否则重新生成。但是每小时它必须再次生成令牌有什么方法可以使客户端静态/单例。
  • 令牌默认只有一个小时有效,所以每个小时你都必须获得一个新令牌。
  • 您使用的是哪个版本的实体框架?我认为 EF 核心的适当方法是使用拦截器
  • 我将 ef core 用于 azure functions core 3.1 LTS。

标签: azure-functions azure-sql-database azure-managed-identity defaultazurecredential


【解决方案1】:

感谢Thomas 和J Weezy 发布您的suggestion 作为回答以帮助其他社区成员。

“注意:您需要将机密迁移到 KeyVault。在这种情况下,我们将其命名为 AzureSqlSecret。这是为了检索数据库用户的凭据。

调用AzureAuthenticationInterceptor的Entities类构造函数如下:

public ProjectNameEntities() :
    base(new DbContextOptionsBuilder<ProjectNameEntities>()
        .UseSqlServer(ConfigurationManager.ConnectionStrings["ProjectNameEntities"].ConnectionString)
        .AddInterceptors(new AzureAuthenticationInterceptor())
        .Options)
{ }

AzureAuthenticationInterceptor:

#region NameSpaces
using Azure.Core;
using Azure.Identity;
using Azure.Security.KeyVault.Secrets;
using Microsoft.Data.SqlClient;
using Microsoft.EntityFrameworkCore.Diagnostics;
using Microsoft.IdentityModel.Clients.ActiveDirectory;
using System;
using System.Configuration;
using System.Data.Common;
using System.Threading;
using System.Threading.Tasks;
#endregion

namespace <ProjectName>.DataAccess.Helpers
{
    public class AzureAuthenticationInterceptor : DbConnectionInterceptor
    {
        #region Constructor
        public AzureAuthenticationInterceptor()
        {
            SecretClientOptions objSecretClientOptions;
            string strAzureKeyVaultResourceIdentifier;
            string strAzureKeyVault;
            string strAzureKeyVaultUri;

            strAzureKeyVaultResourceIdentifier = ConfigurationManager.AppSettings["Azure:ResourceIdentifiers:KeyVault"];
            strAzureKeyVault = ConfigurationManager.AppSettings["Azure:KeyVaults:TaxPaymentSystem"];
            strAzureKeyVaultUri = strAzureKeyVaultResourceIdentifier.Replace("{0}", strAzureKeyVault);

            // Set the options on the SecretClient. These are default values that are recommended by Microsoft.
            objSecretClientOptions = new SecretClientOptions()
            {
                Retry =
                {
                    Delay= TimeSpan.FromSeconds(2),
                    MaxDelay = TimeSpan.FromSeconds(16),
                    MaxRetries = 5,
                    Mode = RetryMode.Exponential
                }
            };

            this.SecretClient = new SecretClient(
                vaultUri: new Uri(strAzureKeyVaultUri),
                credential: new DefaultAzureCredential(), 
                objSecretClientOptions
                );

            this.KeyVaultSecret = this.SecretClient.GetSecret("AzureSqlSecret");
            this.strKeyVaultSecret = this.KeyVaultSecret.Value;

            this.strAzureResourceIdentifierAuthentication = ConfigurationManager.AppSettings["Azure:ResourceIdentifiers:Authentication"];
            this.strAzureResourceIdentifierDatabase = ConfigurationManager.AppSettings["Azure:ResourceIdentifiers:DataBase"];
            this.strClientId = ConfigurationManager.AppSettings["Azure:DatabaseUsername:ClientId"];
            this.strTenantId = ConfigurationManager.AppSettings["Azure:TenantId"];                
        }
        #endregion

        #region Methods
        public override async ValueTask<InterceptionResult> ConnectionOpeningAsync(
            DbConnection objDbConnection,
            ConnectionEventData objEventData,
            InterceptionResult objReturn,
            CancellationToken objCancellationToken = default)
        {
            _ILogger.Debug("Reached the Async Interceptor method");

            if (objDbConnection is SqlConnection objSqlConnection)
            {
                objSqlConnection.AccessToken = GetAccessToken();
            }

            return objReturn;
        }

        public override InterceptionResult ConnectionOpening(
            DbConnection objDbConnection,
            ConnectionEventData objConnectionEventData,
            InterceptionResult objReturn)
        {
            _ILogger.Debug("Reached the non-Async Interceptor method");

            if (objDbConnection is SqlConnection objSqlConnection)
            {
                objSqlConnection.AccessToken = GetAccessToken();
            }

            return objReturn;
        }

        private string GetAccessToken()
        {
            AuthenticationContext objAuthenticationContext;
            AuthenticationResult objAuthenticationResult;
            ClientCredential objClientCredential;

            objAuthenticationContext = new AuthenticationContext(string.Format("{0}/{1}"
                                                                                , this.strAzureResourceIdentifierAuthentication
                                                                                , this.strTenantId));
            objClientCredential = new ClientCredential(this.strClientId, this.strKeyVaultSecret);
            objAuthenticationResult = objAuthenticationContext.AcquireTokenAsync(this.strAzureResourceIdentifierDatabase, objClientCredential).Result;
            return objAuthenticationResult.AccessToken;
        }
        #endregion

        #region Properties
        readonly <ProjectName>.Common.Logging.ILogger _ILogger = <ProjectName>.Common.Logging.LogWrapper.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType);
        private SecretClient SecretClient;
        private KeyVaultSecret KeyVaultSecret;
        private string strAzureResourceIdentifierDatabase;
        private string strAzureResourceIdentifierAuthentication;
        private string strKeyVaultSecret;
        private string strClientId;
        private string strTenantId;
        #endregion
    }
}

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2019-09-08
    • 2021-09-01
    • 2021-10-30
    • 1970-01-01
    • 2021-11-30
    • 2021-09-19
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多