كيفية الاستفادة من الهوية المدارة الخاصة بتطبيق Service Fabric للوصول إلى خدمات Azure

يمكن لتطبيقات Service Fabric الاستفادة من الهويات المدارة للوصول إلى موارد Azure الأخرى التي تدعم المصادقة المستندة إلى معرف Microsoft Entra. يمكن للتطبيق الحصول على رمز وصول يمثل هويته، والتي قد يتم تعيينها من النظام أو من المستخدم، واستخدامها كرمز مميز 'للمالك' للمصادقة على خدمة أخرى - تُعرف أيضاً باسم خادم الموارد المحمي. يمثل الرمز المميز الهوية المعينة لتطبيق Service Fabric، وسيتم إصداره فقط لموارد Azure (بما في ذلك تطبيقات SF) التي تشارك هذه الهوية. ارجع إلى وثائق نظرة عامة على الهوية المُدارة للحصول على وصف تفصيلي للهويات المُدارة، بالإضافة إلى التمييز بين الهويات المعينة من النظام والهويات المعينة من المستخدم. سنشير إلى تطبيق Service Fabric الممكّن بهوية مُدارة باعتباره تطبيق العميل في جميع أنحاء هذه المقالة.

شاهد نموذج تطبيق مصاحب يوضح استخدام الهويات المُدارة لتطبيق Service Fabric التي تم تعيينها من النظام والتي تم تعيينها من المستخدم مع Reliable Services والحاويات.

هام

تمثل الهوية المدارة الاقتران بين مورد Azure ومدير الخدمة في مستأجر Microsoft Entra المقابل المرتبط بالاشتراك الذي يحتوي على المورد. على هذا النحو، في سياق Service Fabric، يتم دعم الهويات المدارة فقط للتطبيقات المُوزعة على هيئة موارد Azure.

هام

قبل استخدام الهوية المُدارة لتطبيق Service Fabric، يجب منح تطبيق العميل حق الوصول إلى المورد المحمي. يرجى الرجوع إلى قائمة خدمات Azure التي تدعم مصادقة Microsoft Entra للتحقق من الدعم، ثم إلى وثائق الخدمة المعنية للحصول على خطوات محددة لمنح هوية الوصول إلى الموارد ذات الاهتمام.

الاستفادة من هوية مدارة باستخدام هوية Azure.

تدعم Azure Identity SDK الآن Service Fabric. يجعل استخدام Azure.Identity كتابة التعليمات البرمجية لاستخدام الهويات المُدارة لتطبيق Service Fabric أسهل لأنها تتعامل مع جلب الرموز المميزة، ورموز التخزين المؤقت المميزة، ومصادقة الخادم. أثناء الوصول إلى معظم موارد Azure، يتم إخفاء مفهوم الرمز المميز.

يتوفرService Fabric دعم بالإصدارات التالية لهذه اللغات:

عينة #C لتهيئة بيانات الاعتماد واستخدام بيانات الاعتماد لجلب بيانات سرية من Azure Key Vault:

using Azure.Identity;
using Azure.Security.KeyVault.Secrets;

namespace MyMIService
{
    internal sealed class MyMIService : StatelessService
    {
        protected override async Task RunAsync(CancellationToken cancellationToken)
        {
            try
            {
                // Load the service fabric application managed identity assigned to the service
                ManagedIdentityCredential creds = new ManagedIdentityCredential();

                // Create a client to keyvault using that identity
                SecretClient client = new SecretClient(new Uri("https://mykv.vault.azure.net/"), creds);

                // Fetch a secret
                KeyVaultSecret secret = (await client.GetSecretAsync("mysecret", cancellationToken: cancellationToken)).Value;
            }
            catch (CredentialUnavailableException e)
            {
                // Handle errors with loading the Managed Identity
            }
            catch (RequestFailedException)
            {
                // Handle errors with fetching the secret
            }
            catch (Exception e)
            {
                // Handle generic errors
            }
        }
    }
}

الحصول على رمز مميز للوصول باستخدام REST API

في المجموعات الممكّنة للهوية المُدارة، يعرض وقت تشغيل Service Fabric نقطة نهاية المضيف المحلي التي يمكن للتطبيقات استخدامها للحصول على رموز مميزة للوصول. نقطة النهاية متاحة على كل عقدة في نظام المجموعة، ويمكن الوصول إليها من جميع الكيانات على تلك العقدة. يمكن للمتصلين المصرح لهم الحصول على رموز الوصول المميزة عن طريق استدعاء هذه النقطة للنهاية وتقديم رمز المصادقة؛ يتم إنشاء التعليمة البرمجية بواسطة وقت تشغيل Service Fabric لكل تنشيط حزمة تعليمة برمجية لخدمة مميزة، وهو مرتبط بعمر العملية التي تستضيف حزمة التعليمة البرمجية لتلك الخدمة.

على وجه التحديد، سيتم إنشاء بيئة خدمة Service Fabric الممكّنة للهوية المُدارة بالمتغيرات التالية:

  • 'IDENTITY_ENDPOINT': نقطة نهاية المضيف المحلي المطابقة للهوية المدارة للخدمة
  • 'IDENTITY_HEADER': رمز مصادقة فريد يمثل الخدمة على العقدة الحالية
  • 'IDENTITY_SERVER_THUMBPRINT': بصمة الإبهام لخادم الهوية المُدار من "تصميم الخدمة"

هام

يجب أن يأخذ رمز التطبيق في الاعتبار قيمة متغير البيئة 'IDENTITY_HEADER' على أنها بيانات حساسة - لا ينبغي تسجيلها أو نشرها بأي طريقة أخرى. لا يحتوي رمز المصادقة على أي قيمة خارج العقدة المحلية، أو بعد انتهاء العملية المضيفة للخدمة، ولكنه يمثل هوية خدمة Service Fabric، ولذا يجب التعامل معه بنفس الاحتياطات مثل رمز الوصول المميز نفسه.

للحصول على رمز مميز، يقوم العميل بالخطوات التالية:

  • تشكل URI عن طريق ربط نقطة نهاية الهوية المُدارة (قيمة IDENTITY_ENDPOINT) بإصدار API والمورد (الجمهور) المطلوب للرمز المميز
  • إنشاء طلب GET http(s) لمعرف URI المحدد
  • يضيف منطق التحقق من صحة شهادة الخادم المناسب
  • يضيف رمز المصادقة (قيمة IDENTITY_HEADER) كرأس للطلب
  • يرسل الطلب

ستحتوي الاستجابة الناجحة على حمولة JSON تمثل رمز الوصول المميز الناتج، بالإضافة إلى البيانات التعريف التي تصفها. سيتضمن الرد الفاشل أيضاً شرحاً للفشل. انظر أدناه للحصول على تفاصيل إضافية عن معالجة الخطأ.

سيتم تخزين رموز الوصول مؤقتاً بواسطة Service Fabric على مستويات مختلفة (العقدة، نظام المجموعة، خدمة مزود الموارد)، لذلك لا تعني الاستجابة الناجحة بالضرورة أن الرمز المميز تم إصداره مباشرة استجابة لطلب تطبيق المستخدم. سيتم تخزين الرموز المميزة في ذاكرة التخزين المؤقت لمدة تقل عن عمرها، وبالتالي يتم ضمان حصول التطبيق على رمز مميز صالح. من المستحسن أن يخزن رمز التطبيق نفسه مؤقتاً أي رموز وصول يكتسبها؛ يجب أن يتضمن مفتاح التخزين المؤقت (اشتقاق) الجمهور.

طلب عينة:

GET 'https://localhost:2377/metadata/identity/oauth2/token?api-version=2019-07-01-preview&resource=https://vault.azure.net/' HTTP/1.1 Secret: 912e4af7-77ba-4fa5-a737-56c8e3ace132

الموقع:

العنصر الوصف
GET فعل "HTTP"، الذي يُشير إلى رغبتك في استرداد البيانات من نقطة النهاية. في هذه الحالة، يكون رمز الوصول هو OAuth.
https://localhost:2377/metadata/identity/oauth2/token نقطة نهاية الهوية المُدارة لتطبيقات Service Fabric، المتوفرة عبر متغير البيئة IDENTITY_ENDPOINT.
api-version معلمة سلسلة الاستعلام، تحدد إصدار API لخدمة الرمز المُدار للهوية؛ القيمة الوحيدة المقبولة حالياً هي 2019-07-01-preview، وهي عرضة للتغيير.
resource معلمة سلسلة استعلام، تشير إلى عنوان URI الخاص بمعرف التطبيق للمورد الهدف. سينعكس هذا على أنه مطالبة aud (الجمهور) بالرمز الذي تم إصداره. يطلب هذا المثال رمزاً مميزاً للوصول إلى Azure Key Vault، الذي يكون معرف التطبيق الخاص به هو https://vault.azure.net/.
Secret حقل رأس طلب HTTP، مطلوب من Service Fabric Managed Identity Token Service لخدمات Service Fabric لمصادقة المتصل. يتم توفير هذه القيمة من خلال وقت تشغيل SF عبر متغير البيئة IDENTITY_HEADER.

نموذج الاستجابة:

HTTP/1.1 200 OK
Content-Type: application/json
{
    "token_type":  "Bearer",
    "access_token":  "eyJ0eXAiO...",
    "expires_on":  1565244611,
    "resource":  "https://vault.azure.net/"
}

الموقع:

العنصر الوصف
token_type نوع الرمز المميز في هذه الحالة، يكون الرمز المميز لوصول "Bearer"، والذي يعني أن مقدم ('المالك') لهذا الرمز المميز هو الموضوع المقصود للرمز المميز.
access_token رمز الوصول المميز المطلوب. عند استدعاء واجهة برمجة تطبيقات REST المؤمنة، يتم تضمين الرمز المميز في حقل عنوان طلب Authorization كرمز مميز "للحامل"، مما يسمح لواجهة برمجة التطبيقات بمصادقة المتصل.
expires_on الطابع الزمني لانتهاء صلاحية رمز الوصول؛ يتم تمثيله على أنه عدد الثواني من "1970-01-01T0:0:0Z UTC" ويتوافق مع مطالبة الرمز المميز exp. في هذه الحالة، تنتهي صلاحية الرمز المميز في 2019-08-08T06:10:11+00:00 (في RFC 3339)
resource المورد الذي تم إصدار رمز الوصول المميز من أجله، محدد عبر معلمة سلسلة الاستعلام resource للطلب؛ يتوافق مع مطالبة الرمز المميز 'aud'.

الحصول على رمز الوصول المميز باستخدام C #

ما ورد أعلاه يصبح في #C:

namespace Azure.ServiceFabric.ManagedIdentity.Samples
{
    using System;
    using System.Net.Http;
    using System.Text;
    using System.Threading;
    using System.Threading.Tasks;
    using System.Web;
    using Newtonsoft.Json;

    /// <summary>
    /// Type representing the response of the SF Managed Identity endpoint for token acquisition requests.
    /// </summary>
    [JsonObject]
    public sealed class ManagedIdentityTokenResponse
    {
        [JsonProperty(Required = Required.Always, PropertyName = "token_type")]
        public string TokenType { get; set; }

        [JsonProperty(Required = Required.Always, PropertyName = "access_token")]
        public string AccessToken { get; set; }

        [JsonProperty(PropertyName = "expires_on")]
        public string ExpiresOn { get; set; }

        [JsonProperty(PropertyName = "resource")]
        public string Resource { get; set; }
    }

    /// <summary>
    /// Sample class demonstrating access token acquisition using Managed Identity.
    /// </summary>
    public sealed class AccessTokenAcquirer
    {
        /// <summary>
        /// Acquire an access token.
        /// </summary>
        /// <returns>Access token</returns>
        public static async Task<string> AcquireAccessTokenAsync()
        {
            var managedIdentityEndpoint = Environment.GetEnvironmentVariable("IDENTITY_ENDPOINT");
            var managedIdentityAuthenticationCode = Environment.GetEnvironmentVariable("IDENTITY_HEADER");
            var managedIdentityServerThumbprint = Environment.GetEnvironmentVariable("IDENTITY_SERVER_THUMBPRINT");
            // Latest api version, 2019-07-01-preview is still supported.
            var managedIdentityApiVersion = Environment.GetEnvironmentVariable("IDENTITY_API_VERSION");
            var managedIdentityAuthenticationHeader = "secret";
            var resource = "https://management.azure.com/";

            var requestUri = $"{managedIdentityEndpoint}?api-version={managedIdentityApiVersion}&resource={HttpUtility.UrlEncode(resource)}";

            var requestMessage = new HttpRequestMessage(HttpMethod.Get, requestUri);
            requestMessage.Headers.Add(managedIdentityAuthenticationHeader, managedIdentityAuthenticationCode);
            
            var handler = new HttpClientHandler();
            handler.ServerCertificateCustomValidationCallback = (httpRequestMessage, cert, certChain, policyErrors) =>
            {
                // Do any additional validation here
                if (policyErrors == SslPolicyErrors.None)
                {
                    return true;
                }
                return 0 == string.Compare(cert.GetCertHashString(), managedIdentityServerThumbprint, StringComparison.OrdinalIgnoreCase);
            };

            try
            {
                var response = await new HttpClient(handler).SendAsync(requestMessage)
                    .ConfigureAwait(false);

                response.EnsureSuccessStatusCode();

                var tokenResponseString = await response.Content.ReadAsStringAsync()
                    .ConfigureAwait(false);

                var tokenResponseObject = JsonConvert.DeserializeObject<ManagedIdentityTokenResponse>(tokenResponseString);

                return tokenResponseObject.AccessToken;
            }
            catch (Exception ex)
            {
                string errorText = String.Format("{0} \n\n{1}", ex.Message, ex.InnerException != null ? ex.InnerException.Message : "Acquire token failed");

                Console.WriteLine(errorText);
            }

            return String.Empty;
        }
    } // class AccessTokenAcquirer
} // namespace Azure.ServiceFabric.ManagedIdentity.Samples

الوصول إلى Key Vault من تطبيق Service Fabric باستخدام Managed Identity

يعتمد هذا النموذج على ما ورد أعلاه لإثبات الوصول إلى البيانات السرية لمخزن في Key Vault باستخدام هوية مُدارة.

        /// <summary>
        /// Probe the specified secret, displaying metadata on success.  
        /// </summary>
        /// <param name="vault">vault name</param>
        /// <param name="secret">secret name</param>
        /// <param name="version">secret version id</param>
        /// <returns></returns>
        public async Task<string> ProbeSecretAsync(string vault, string secret, string version)
        {
            // initialize a KeyVault client with a managed identity-based authentication callback
            var kvClient = new Microsoft.Azure.KeyVault.KeyVaultClient(new Microsoft.Azure.KeyVault.KeyVaultClient.AuthenticationCallback((a, r, s) => { return AuthenticationCallbackAsync(a, r, s); }));

            Log(LogLevel.Info, $"\nRunning with configuration: \n\tobserved vault: {config.VaultName}\n\tobserved secret: {config.SecretName}\n\tMI endpoint: {config.ManagedIdentityEndpoint}\n\tMI auth code: {config.ManagedIdentityAuthenticationCode}\n\tMI auth header: {config.ManagedIdentityAuthenticationHeader}");
            string response = String.Empty;

            Log(LogLevel.Info, "\n== {DateTime.UtcNow.ToString()}: Probing secret...");
            try
            {
                var secretResponse = await kvClient.GetSecretWithHttpMessagesAsync(vault, secret, version)
                    .ConfigureAwait(false);

                if (secretResponse.Response.IsSuccessStatusCode)
                {
                    // use the secret: secretValue.Body.Value;
                    response = String.Format($"Successfully probed secret '{secret}' in vault '{vault}': {PrintSecretBundleMetadata(secretResponse.Body)}");
                }
                else
                {
                    response = String.Format($"Non-critical error encountered retrieving secret '{secret}' in vault '{vault}': {secretResponse.Response.ReasonPhrase} ({secretResponse.Response.StatusCode})");
                }
            }
            catch (Microsoft.Rest.ValidationException ve)
            {
                response = String.Format($"encountered REST validation exception 0x{ve.HResult.ToString("X")} trying to access '{secret}' in vault '{vault}' from {ve.Source}: {ve.Message}");
            }
            catch (KeyVaultErrorException kvee)
            {
                response = String.Format($"encountered KeyVault exception 0x{kvee.HResult.ToString("X")} trying to access '{secret}' in vault '{vault}': {kvee.Response.ReasonPhrase} ({kvee.Response.StatusCode})");
            }
            catch (Exception ex)
            {
                // handle generic errors here
                response = String.Format($"encountered exception 0x{ex.HResult.ToString("X")} trying to access '{secret}' in vault '{vault}': {ex.Message}");
            }

            Log(LogLevel.Info, response);

            return response;
        }

        /// <summary>
        /// KV authentication callback, using the application's managed identity.
        /// </summary>
        /// <param name="authority">The expected issuer of the access token, from the KV authorization challenge.</param>
        /// <param name="resource">The expected audience of the access token, from the KV authorization challenge.</param>
        /// <param name="scope">The expected scope of the access token; not currently used.</param>
        /// <returns>Access token</returns>
        public async Task<string> AuthenticationCallbackAsync(string authority, string resource, string scope)
        {
            Log(LogLevel.Verbose, $"authentication callback invoked with: auth: {authority}, resource: {resource}, scope: {scope}");
            var encodedResource = HttpUtility.UrlEncode(resource);

            // This sample does not illustrate the caching of the access token, which the user application is expected to do.
            // For a given service, the caching key should be the (encoded) resource uri. The token should be cached for a period
            // of time at most equal to its remaining validity. The 'expires_on' field of the token response object represents
            // the number of seconds from Unix time when the token will expire. You may cache the token if it will be valid for at
            // least another short interval (1-10s). If its expiration will occur shortly, don't cache but still return it to the 
            // caller. The MI endpoint will not return an expired token.
            // Sample caching code:
            //
            // ManagedIdentityTokenResponse tokenResponse;
            // if (responseCache.TryGetCachedItem(encodedResource, out tokenResponse))
            // {
            //     Log(LogLevel.Verbose, $"cache hit for key '{encodedResource}'");
            //
            //     return tokenResponse.AccessToken;
            // }
            //
            // Log(LogLevel.Verbose, $"cache miss for key '{encodedResource}'");
            //
            // where the response cache is left as an exercise for the reader. MemoryCache is a good option, albeit not yet available on .net core.

            var requestUri = $"{config.ManagedIdentityEndpoint}?api-version={config.ManagedIdentityApiVersion}&resource={encodedResource}";
            Log(LogLevel.Verbose, $"request uri: {requestUri}");

            var requestMessage = new HttpRequestMessage(HttpMethod.Get, requestUri);
            requestMessage.Headers.Add(config.ManagedIdentityAuthenticationHeader, config.ManagedIdentityAuthenticationCode);
            Log(LogLevel.Verbose, $"added header '{config.ManagedIdentityAuthenticationHeader}': '{config.ManagedIdentityAuthenticationCode}'");

            var response = await httpClient.SendAsync(requestMessage)
                .ConfigureAwait(false);
            Log(LogLevel.Verbose, $"response status: success: {response.IsSuccessStatusCode}, status: {response.StatusCode}");

            response.EnsureSuccessStatusCode();

            var tokenResponseString = await response.Content.ReadAsStringAsync()
                .ConfigureAwait(false);

            var tokenResponse = JsonConvert.DeserializeObject<ManagedIdentityTokenResponse>(tokenResponseString);
            Log(LogLevel.Verbose, "deserialized token response; returning access code..");

            // Sample caching code (continuation):
            // var expiration = DateTimeOffset.FromUnixTimeSeconds(Int32.Parse(tokenResponse.ExpiresOn));
            // if (expiration > DateTimeOffset.UtcNow.AddSeconds(5.0))
            //    responseCache.AddOrUpdate(encodedResource, tokenResponse, expiration);

            return tokenResponse.AccessToken;
        }

        private string PrintSecretBundleMetadata(SecretBundle bundle)
        {
            StringBuilder strBuilder = new StringBuilder();

            strBuilder.AppendFormat($"\n\tid: {bundle.Id}\n");
            strBuilder.AppendFormat($"\tcontent type: {bundle.ContentType}\n");
            strBuilder.AppendFormat($"\tmanaged: {bundle.Managed}\n");
            strBuilder.AppendFormat($"\tattributes:\n");
            strBuilder.AppendFormat($"\t\tenabled: {bundle.Attributes.Enabled}\n");
            strBuilder.AppendFormat($"\t\tnbf: {bundle.Attributes.NotBefore}\n");
            strBuilder.AppendFormat($"\t\texp: {bundle.Attributes.Expires}\n");
            strBuilder.AppendFormat($"\t\tcreated: {bundle.Attributes.Created}\n");
            strBuilder.AppendFormat($"\t\tupdated: {bundle.Attributes.Updated}\n");
            strBuilder.AppendFormat($"\t\trecoveryLevel: {bundle.Attributes.RecoveryLevel}\n");

            return strBuilder.ToString();
        }

        private enum LogLevel
        {
            Info,
            Verbose
        };

        private void Log(LogLevel level, string message)
        {
            if (level != LogLevel.Verbose
                || config.DoVerboseLogging)
            {
                Console.WriteLine(message);
            }
        }

معالجة الخطأ

يشير حقل 'التعليمة البرمجية للحالة' الخاص برأس استجابة HTTP إلى حالة نجاح الطلب؛ تشير الحالة '200 OK' إلى النجاح، وستتضمن الاستجابة رمز الوصول المميز كما هو موضح أعلاه. فيما يلي تعداد قصير للردود المحتملة على الخطأ.

رمز الحالة سبب الخطأ كيفية التعامل
404 لم يتم العثور. رمز مصادقة غير معروف، أو لم يتم تعيين هوية مُدارة للتطبيق. صحِّح إعداد التطبيق أو رمز الحصول على الرمز المميز.
429 طلبات كثيرة جدًا. تم الوصول إلى حد التقييد، الذي يفرضه معرف Microsoft Entra أو SF. أعد المحاولة باستخدام التراجع الأسي. انظر التوجيه أدناه.
4xx خطأ في الطلب. معلمة واحدة أو أكثر من معلمات الطلب غير صحيحة. لا تحاول. افحص تفاصيل الخطأ للحصول على مزيد من المعلومات. أخطاء 4xx هي أخطاء وقت التصميم.
خطأ 5xx من الخدمة. أرجع النظام الفرعي للهوية المدارة أو معرف Microsoft Entra خطأ عابرا. من الآمن إعادة المحاولة بعد فترة قصيرة. قد تواجه حالة تقييد (429) عند إعادة المحاولة.

في حالة حدوث خطأ، يحتوي نص استجابة HTTP المقابل على عنصر JSON بتفاصيل الخطأ:

العنصر الوصف
الكود الرمز الخاص بالخطأ
correlationId معرف الارتباط الذي يمكن استخدامه لتصحيح الأخطاء.
رسالة وصف مطول للخطأ. يمكن تغيير أوصاف الخطأ في أي وقت. لا تعتمد على رسالة الخطأ نفسها.

خطأ في العينة:

{"error":{"correlationId":"7f30f4d3-0f3a-41e0-a417-527f21b3848f","code":"SecretHeaderNotFound","message":"Secret is not found in the request headers."}}

فيما يلي قائمة بأخطاء Service Fabric النموذجية الخاصة بالهويات المدارة:

الرمز رسالة ‏‏الوصف
SecretHeaderNotFound لم يتم العثور على البيانات السرية في رؤوس الطلبات. لم يتم توفير تعليمة برمجية للمصادقة مع الطلب.
ManagedIdentityNotFound لم يتم العثور على الهوية المُدارة لمضيف التطبيق المحدد. التطبيق ليست له هوية، أو أن رمز المصادقة غير معروف.
ArgumentNullOrEmpty يجب ألا تكون المعلمة 'مورد' خالية أو سلسلة فارغة. لم يتم توفير المورد (الجمهور) في الطلب.
InvalidApiVersion إصدار api '' غير مدعوم الإصدار المدعوم هو '2019-07-01-الإصدار الأولي'. إصدار API مفقود أو غير مدعوم المحدد في URI للطلب.
InternalServerError حدث خطأ. تمت مصادفة خطأ في النظام الفرعي للهوية المدارة، ربما خارج مكدس Service Fabric. السبب الأكثر احتمالاً هو تحديد قيمة غير صحيحة للمورد (check for trailing '/'?)

توجيه إعادة المحاولة

عادةً ما يكون رمز الخطأ الوحيد القابل لإعادة المحاولة هو 429 (Too Many Requests)؛ قد تكون أخطاء الخادم الداخلية/التعليمات البرمجية للخطأ 5xx قابلة لإعادة المحاولة، على الرغم من أن السبب قد يكون دائماً.

تنطبق حدود التقييد على عدد الاتصالات المُجراة على النظام الفرعي للهوية المدارة - على وجه التحديد تبعيات 'المصدر' (خدمة Managed Identity Azure، أو خدمة الرمز المميز الآمن). يقوم Service Fabric بتخزين الرموز المميزة على مستويات مختلفة في البنية الأساسية، ولكن بالنظر إلى الطبيعة الموزعة للمكونات المعنية، قد يواجه المتصل استجابات تقييد غير متسقة (على سبيل المثال، يتم التحكم في عقدة/مثيل واحد لتطبيق، ولكن ليس على عقدة مختلفة أثناء طلب رمز مميز لنفس الهوية.) عند تعيين شرط التقييد، قد تفشل الطلبات اللاحقة من نفس التطبيق مع رمز حالة HTTP 429 (طلبات كثيرة جداً) حتى يتم مسح الشرط.

يوصى بإعادة محاولة الطلبات الفاشلة بسبب التقييد بتراجع أسي، على النحو التالي:

فهرس المكالمات إجراءات استلام 429
1 انتظر ثانية واحدة ثم أعد المحاولة
2 انتظر ثانيتين ثم أعد المحاولة
3 انتظر 4 ثوانٍ وأعد المحاولة
4 انتظر 8 ثوان ثم أعد المحاولة
4 انتظر 8 ثوان ثم أعد المحاولة
5 انتظر 16 ثانية وأعد المحاولة

معرفات الموارد لخدمات Azure

راجع خدمات Azure التي تدعم مصادقة Microsoft Entra للحصول على قائمة بالموارد التي تدعم معرف Microsoft Entra ومعرفات الموارد الخاصة بها.

الخطوات التالية