Samouczek: zabezpieczanie usługi Azure Remote Rendering i magazynu modelu

Z tego samouczka dowiesz się, jak wykonywać następujące czynności:

  • Zabezpieczanie usługi Azure Blob Storage zawierającej modele usługi Azure Remote Rendering
  • Uwierzytelnianie za pomocą identyfikatora Entra firmy Microsoft w celu uzyskania dostępu do wystąpienia usługi Azure Remote Rendering
  • Używanie poświadczeń platformy Azure na potrzeby uwierzytelniania usługi Azure Remote Rendering

Wymagania wstępne

Dlaczego potrzebne są dodatkowe zabezpieczenia

Bieżący stan aplikacji i jej dostęp do zasobów platformy Azure wygląda następująco:

Initial security

Zarówno "AccountID + AccountKey", jak i "ADRES URL + token SAS" zasadniczo przechowują nazwę użytkownika i hasło razem. Jeśli na przykład ujawniono wartość "AccountID + AccountKey", osoba atakująca mogłaby korzystać z zasobów usługi ARR bez zgody użytkownika.

Zabezpieczanie zawartości w usłudze Azure Blob Storage

Usługa Azure Remote Rendering może bezpiecznie uzyskać dostęp do zawartości usługi Azure Blob Storage przy użyciu prawidłowej konfiguracji. Zobacz Instrukcje: łączenie kont magazynu w celu skonfigurowania wystąpienia usługi Azure Remote Rendering przy użyciu kont magazynu obiektów blob.

W przypadku korzystania z połączonego magazynu obiektów blob do ładowania modeli używa się nieco innych metod:

var loadModelParams = new LoadModelFromSasOptions(modelPath, modelEntity);
var task = ARRSessionService.CurrentActiveSession.Connection.LoadModelFromSasAsync(loadModelParams);

Powyższe wiersze używają FromSas wersji parametrów i akcji sesji. Muszą one zostać przekonwertowane na wersje inne niż SAS:

var loadModelParams = LoadModelOptions.CreateForBlobStorage(storageAccountPath, blobName, modelPath, modelEntity);
var task = ARRSessionService.CurrentActiveSession.Connection.LoadModelAsync(loadModelParams);

Zmodyfikujmy element RemoteRenderingCoordinator , aby załadować model niestandardowy z połączonego konta magazynu obiektów blob.

  1. Jeśli jeszcze tego nie zrobiono, ukończ instrukcje : łączenie kont magazynu w celu udzielenia wystąpieniu usługi ARR uprawnienia dostępu do wystąpienia usługi Blob Storage.

  2. Dodaj następującą zmodyfikowaną metodę LoadModel do remoteRenderingCoordinator tuż poniżej bieżącej metody LoadModel:

    /// <summary>
    /// Loads a model from blob storage that has been linked to the ARR instance
    /// </summary>
    /// <param name="storageAccountName">The storage account name, this contains the blob containers </param>
    /// <param name="blobName">The blob container name, i.e. arroutput</param>
    /// <param name="modelPath">The relative path inside the container to the model, i.e. test/MyCustomModel.arrAsset</param>
    /// <param name="parent">The parent Transform for this remote entity</param>
    /// <param name="progress">A call back method that accepts a float progress value [0->1]</param>
    /// <returns></returns>
    public async Task<Entity> LoadModel(string storageAccountName, string blobName, string modelPath, UnityEngine.Transform parent = null, Action<float> progress = null)
    {
        //Create a root object to parent a loaded model to
        var modelEntity = ARRSessionService.CurrentActiveSession.Connection.CreateEntity();
    
        //Get the game object representation of this entity
        var modelGameObject = modelEntity.GetOrCreateGameObject(UnityCreationMode.DoNotCreateUnityComponents);
    
        //Ensure the entity will sync its transform with the server
        var sync = modelGameObject.GetComponent<RemoteEntitySyncObject>();
        sync.SyncEveryFrame = true;
    
        //Parent the new object under the defined parent
        if (parent != null)
        {
            modelGameObject.transform.SetParent(parent, false);
            modelGameObject.name = parent.name + "_Entity";
        }
    
        //Load a model that will be parented to the entity
        var loadModelParams = LoadModelOptions.CreateForBlobStorage($"{storageAccountName}.blob.core.windows.net", blobName, modelPath, modelEntity);
        var loadModelAsync = ARRSessionService.CurrentActiveSession.Connection.LoadModelAsync(loadModelParams, progress);
        var result = await loadModelAsync;
        return modelEntity;
    }
    

    Ten kod jest identyczny z oryginalną LoadModel metodą, jednak zamieniliśmy wersję sygnatury dostępu współdzielonego wywołań metody na wersje inne niż SAS.

    Dodatkowe dane wejściowe storageAccountName i blobName zostały również dodane do argumentów. Wywołamy tę nową metodę LoadModel z innej metody podobnej do pierwszej metody LoadTestModel utworzonej w pierwszym samouczku.

  3. Dodaj następującą metodę do remoteRenderingCoordinator tuż po modelu LoadTestModel

    private bool loadingLinkedCustomModel = false;
    
    [SerializeField]
    private string storageAccountName;
    public string StorageAccountName {
        get => storageAccountName.Trim();
        set => storageAccountName = value;
    }
    
    [SerializeField]
    private string blobContainerName;
    public string BlobContainerName {
        get => blobContainerName.Trim();
        set => blobContainerName = value;
    }
    
    [SerializeField]
    private string modelPath;
    public string ModelPath {
        get => modelPath.Trim();
        set => modelPath = value;
    }
    
    [ContextMenu("Load Linked Custom Model")]
    public async void LoadLinkedCustomModel()
    {
        if (CurrentCoordinatorState != RemoteRenderingState.RuntimeConnected)
        {
            Debug.LogError("Please wait for the runtime to connect before loading the test model. Try again later.");
            return;
        }
        if (loadingLinkedCustomModel)
        {
            Debug.Log("Linked Test model already loading or loaded!");
            return;
        }
        loadingLinkedCustomModel = true;
    
        // Create a parent object to use for positioning
        GameObject testParent = new GameObject("LinkedCustomModel");
        testParent.transform.position = new Vector3(0f, 0f, 3f);
    
        await LoadModel(StorageAccountName, BlobContainerName, ModelPath, testParent.transform, (progressValue) => Debug.Log($"Loading Test Model progress: {Math.Round(progressValue * 100, 2)}%"));
    }
    

    Ten kod dodaje trzy dodatkowe zmienne ciągu do składnika RemoteRenderingCoordinator . Screenshot that highlights the Storage Account Name, Blob Container Name, and Model Path of the RemoteRenderingCoordinator component.

  4. Dodaj wartości do składnika RemoteRenderingCoordinator . Po przejściu do przewodnika Szybki start dotyczącego konwersji modelu wartości powinny być następujące:

    • Nazwa konta magazynu: Nazwa konta magazynu, globalnie unikatowa nazwa wybranego konta magazynu. W przewodniku Szybki start był to arrtutorialstorage, twoja wartość jest inna.
    • Nazwa kontenera obiektów blob: arroutput, kontener usługi Blob Storage
    • Ścieżka modelu: kombinacja parametrów "outputFolderPath" i "outputAssetFileName" zdefiniowanych w pliku arrconfig.json . W przewodniku Szybki start był to ciąg "outputFolderPath":"converted/robot", "outputAssetFileName": "robot.arrAsset". Co spowodowałoby, że wartość ścieżki modelu "converted/robot/robot.arrAsset" jest inna.

    Napiwek

    Jeśli uruchomisz skrypt Conversion.ps1 bez argumentu "-UseContainerSas", skrypt zwróci wszystkie powyższe wartości dla tokenu SAS zamiast tokenu SAS. Linked Model

  5. Na razie usuń lub wyłącz model TestModel obiektu GameObject, aby zapewnić miejsce na załadowanie modelu niestandardowego.

  6. Odtwórz scenę i połącz się z sesją zdalną.

  7. Otwórz menu kontekstowe w pozycji RemoteRenderingCoordinator i wybierz pozycję Załaduj połączony model niestandardowy. Load linked model

Te kroki zwiększyły bezpieczeństwo aplikacji przez usunięcie tokenu SAS z aplikacji lokalnej.

Teraz bieżący stan aplikacji i jej dostęp do zasobów platformy Azure wygląda następująco:

Better security

Mamy jeszcze jedno "hasło", AccountKey, które ma być usunięte z aplikacji lokalnej. Można to zrobić przy użyciu uwierzytelniania firmy Microsoft Entra.

Uwierzytelnianie Microsoft Entra

Uwierzytelnianie firmy Microsoft Entra umożliwia określenie, które osoby lub grupy używają usługi ARR w bardziej kontrolowany sposób. Usługa ARR ma wbudowaną obsługę akceptowania tokenów dostępu zamiast używania klucza konta. Tokeny dostępu można traktować jako klucz ograniczony czasowo, który umożliwia odblokowanie tylko niektórych części określonego zasobu, dla którego zażądano.

Skrypt RemoteRenderingCoordinator ma delegata o nazwie ARRCredentialGetter, który zawiera metodę zwracającą obiekt SessionConfiguration , który służy do konfigurowania zarządzania sesją zdalną. Możemy przypisać inną metodę do usługi ARRCredentialGetter, co umożliwia korzystanie z przepływu logowania platformy Azure, generując obiekt SessionConfiguration , który zawiera token dostępu platformy Azure. Ten token dostępu będzie specyficzny dla użytkownika, który się loguje.

  1. Postępuj zgodnie z instrukcjami: Konfigurowanie uwierzytelniania — uwierzytelnianie dla wdrożonych aplikacji, które obejmuje zarejestrowanie nowej aplikacji Firmy Microsoft Entra i skonfigurowanie dostępu do wystąpienia usługi ARR.

  2. Po skonfigurowaniu nowej aplikacji Microsoft Entra sprawdź, czy aplikacja Microsoft Entra wygląda jak na następujących obrazach:

    Aplikacja Firmy Microsoft Entra —> uwierzytelnianieApp authentication

    Microsoft Entra Application —> uprawnienia interfejsu APIApp APIs

  3. Po skonfigurowaniu konta usługi Remote Rendering sprawdź, czy konfiguracja wygląda następująco:

    ARR —> AccessControl (IAM)ARR Role

    Uwaga

    Rola właściciela nie jest wystarczająca do zarządzania sesjami za pośrednictwem aplikacji klienckiej. Dla każdego użytkownika, który chcesz przyznać możliwość zarządzania sesjami, musisz podać rolę Klient usługi Remote Rendering. Dla każdego użytkownika, który chcesz zarządzać sesjami i konwertować modele, musisz podać rolę Remote Rendering Administracja istrator.

Po stronie platformy Azure musimy teraz zmodyfikować sposób łączenia kodu z usługą ARR. Robimy to przez zaimplementowanie wystąpienia baseARRAuthentication, które zwraca nowy obiekt SessionConfiguration . W takim przypadku informacje o koncie są konfigurowane przy użyciu tokenu dostępu platformy Azure.

  1. Utwórz nowy skrypt o nazwie AADAuthentication i zastąp jego kod następującym kodem:

    // Copyright (c) Microsoft Corporation. All rights reserved.
    // Licensed under the MIT License. See LICENSE in the project root for license information.
    
    using Microsoft.Azure.RemoteRendering;
    using Microsoft.Identity.Client;
    using System;
    using System.Linq;
    using System.Threading;
    using System.Threading.Tasks;
    using UnityEngine;
    
    public class AADAuthentication : BaseARRAuthentication
    {
        [SerializeField]
        private string activeDirectoryApplicationClientID;
        public string ActiveDirectoryApplicationClientID
        {
            get => activeDirectoryApplicationClientID.Trim();
            set => activeDirectoryApplicationClientID = value;
        }
    
        [SerializeField]
        private string azureTenantID;
        public string AzureTenantID
        {
            get => azureTenantID.Trim();
            set => azureTenantID = value;
        }
    
        [SerializeField]
        private string azureRemoteRenderingDomain;
        public string AzureRemoteRenderingDomain
        {
            get => azureRemoteRenderingDomain.Trim();
            set => azureRemoteRenderingDomain = value;
        }
    
        [SerializeField]
        private string azureRemoteRenderingAccountID;
        public string AzureRemoteRenderingAccountID
        {
            get => azureRemoteRenderingAccountID.Trim();
            set => azureRemoteRenderingAccountID = value;
        }
    
        [SerializeField]
        private string azureRemoteRenderingAccountDomain;
        public string AzureRemoteRenderingAccountDomain
        {
            get => azureRemoteRenderingAccountDomain.Trim();
            set => azureRemoteRenderingAccountDomain = value;
        }    
    
        public override event Action<string> AuthenticationInstructions;
    
        string authority => "https://login.microsoftonline.com/" + AzureTenantID;
    
        string redirect_uri = "https://login.microsoftonline.com/common/oauth2/nativeclient";
    
        string[] scopes => new string[] { "https://sts.mixedreality.azure.com//.default" };
    
        public void OnEnable()
        {
            RemoteRenderingCoordinator.ARRCredentialGetter = GetARRCredentials;
            this.gameObject.AddComponent<ExecuteOnUnityThread>();
        }
    
        public async override Task<SessionConfiguration> GetARRCredentials()
        {
            var result = await TryLogin();
            if (result != null)
            {
                Debug.Log("Account signin successful " + result.Account.Username);
    
                var AD_Token = result.AccessToken;
    
                return await Task.FromResult(new SessionConfiguration(AzureRemoteRenderingAccountDomain, AzureRemoteRenderingDomain, AzureRemoteRenderingAccountID, "", AD_Token, ""));
            }
            else
            {
                Debug.LogError("Error logging in");
            }
            return default;
        }
    
        private Task DeviceCodeReturned(DeviceCodeResult deviceCodeDetails)
        {
            //Since everything in this task can happen on a different thread, invoke responses on the main Unity thread
            ExecuteOnUnityThread.Enqueue(() =>
            {
                // Display instructions to the user for how to authenticate in the browser
                Debug.Log(deviceCodeDetails.Message);
                AuthenticationInstructions?.Invoke(deviceCodeDetails.Message);
            });
    
            return Task.FromResult(0);
        }
    
        public override async Task<AuthenticationResult> TryLogin()
        {
            var clientApplication = PublicClientApplicationBuilder.Create(ActiveDirectoryApplicationClientID).WithAuthority(authority).WithRedirectUri(redirect_uri).Build();
            AuthenticationResult result = null;
            try
            {
                var accounts = await clientApplication.GetAccountsAsync();
    
                if (accounts.Any())
                {
                    result = await clientApplication.AcquireTokenSilent(scopes, accounts.First()).ExecuteAsync();
    
                    return result;
                }
                else
                {
                    try
                    {
                        result = await clientApplication.AcquireTokenWithDeviceCode(scopes, DeviceCodeReturned).ExecuteAsync(CancellationToken.None);
                        return result;
                    }
                    catch (MsalUiRequiredException ex)
                    {
                        Debug.LogError("MsalUiRequiredException");
                        Debug.LogException(ex);
                    }
                    catch (MsalServiceException ex)
                    {
                        Debug.LogError("MsalServiceException");
                        Debug.LogException(ex);
                    }
                    catch (MsalClientException ex)
                    {
                        Debug.LogError("MsalClientException");
                        Debug.LogException(ex);
                        // Mitigation: Use interactive authentication
                    }
                    catch (Exception ex)
                    {
                        Debug.LogError("Exception");
                        Debug.LogException(ex);
                    }
                }
            }
            catch (Exception ex)
            {
                Debug.LogError("GetAccountsAsync");
                Debug.LogException(ex);
            }
    
            return null;
        }
    }
    

Uwaga

Ten kod nie jest w żaden sposób kompletny i nie jest gotowy do zastosowania komercyjnego. Na przykład co najmniej chcesz dodać możliwość wylogowania się. Można to zrobić przy użyciu Task RemoveAsync(IAccount account) metody udostępnionej przez aplikację kliencka. Ten kod jest przeznaczony tylko do użycia w samouczku. Implementacja będzie specyficzna dla aplikacji.

Kod najpierw próbuje uzyskać token w trybie dyskretnym przy użyciu elementu AquireTokenSilent. To powiedzie się, jeśli użytkownik wcześniej uwierzytelnił tę aplikację. Jeśli nie powiedzie się, przejdź do bardziej zaangażowanej strategii użytkownika.

W tym kodzie używamy przepływu kodu urządzenia w celu uzyskania tokenu dostępu. Ten przepływ umożliwia użytkownikowi zalogowanie się do konta platformy Azure na komputerze lub urządzeniu przenośnym i wysłanie tokenu wynikowego z powrotem do aplikacji HoloLens.

Najważniejszą częścią tej klasy z perspektywy ARR jest ten wiersz:

return await Task.FromResult(new SessionConfiguration(AzureRemoteRenderingAccountDomain, AzureRemoteRenderingDomain, AzureRemoteRenderingAccountID, "", AD_Token, ""));

W tym miejscu utworzymy nowy obiekt SessionConfiguration przy użyciu domeny renderowania zdalnego, identyfikatora konta, domeny konta i tokenu dostępu. Ten token jest następnie używany przez usługę ARR do wykonywania zapytań, tworzenia i dołączania sesji renderowania zdalnego, o ile użytkownik jest autoryzowany na podstawie skonfigurowanych wcześniej uprawnień opartych na rolach.

Po tej zmianie bieżący stan aplikacji i jej dostęp do zasobów platformy Azure wygląda następująco:

Even better security

Ponieważ poświadczenia użytkownika nie są przechowywane na urządzeniu (lub w tym przypadku nawet wprowadzone na urządzeniu), ryzyko narażenia jest niskie. Teraz urządzenie używa określonego przez użytkownika, ograniczonego czasowo tokenu dostępu w celu uzyskania dostępu do usługi ARR, która używa kontroli dostępu (IAM) w celu uzyskania dostępu do usługi Blob Storage. Te dwa kroki usunęły "hasła" z kodu źródłowego i znacznie zwiększyły bezpieczeństwo. Jednak nie jest to najbardziej dostępne zabezpieczenia, przeniesienie modelu i zarządzania sesjami do usługi internetowej jeszcze bardziej zwiększy bezpieczeństwo. Dodatkowe zagadnienia dotyczące zabezpieczeń zostały omówione w rozdziale Gotowość komercyjna .

Testowanie uwierzytelniania entra firmy Microsoft

W edytorze aparatu Unity, gdy uwierzytelnianie Microsoft Entra jest aktywne, należy uwierzytelnić się za każdym razem, gdy uruchamiasz aplikację. Na urządzeniu krok uwierzytelniania odbywa się po raz pierwszy i jest wymagany tylko wtedy, gdy token wygaśnie lub zostanie unieważniony.

  1. Dodaj składnik uwierzytelniania Entra firmy Microsoft do obiektu GameObject RemoteRenderingCoordinator.

    Microsoft Entra auth component

Uwaga

Jeśli używasz ukończonego projektu z repozytorium przykładów ARR, pamiętaj, aby włączyć składnik uwierzytelniania Entra firmy Microsoft, klikając pole wyboru obok jego tytułu.

  1. Wypełnij wartości identyfikatora klienta i identyfikatora dzierżawy. Te wartości można znaleźć na stronie przeglądu rejestracji aplikacji:

    • Identyfikator klienta aplikacji usługi Active Directory to identyfikator aplikacji (klienta) znaleziony w rejestracji aplikacji Firmy Microsoft Entra (zobacz obraz poniżej).
    • Identyfikator dzierżawy platformy Azure to identyfikator katalogu (dzierżawy) znaleziony w rejestracji aplikacji Microsoft Entra (zobacz obraz poniżej).
    • Domena usługi Azure Remote Rendering jest tą samą domeną, której używano w domenie remoteRenderingCoordinator's Remote Rendering.
    • Identyfikator konta usługi Azure Remote Rendering jest tym samym identyfikatorem konta, którego używano dla remoteRenderingCoordinator.
    • Domena konta usługi Azure Remote Rendering jest tą samą domeną konta, której używano w remoteRenderingCoordinator.

    Screenshot that highlights the Application (client) ID and Directory (tenant) ID.

  2. Naciśnij pozycję Odtwórz w edytorze aparatu Unity i wyrażasz zgodę na uruchomienie sesji. Ponieważ składnik uwierzytelniania Entra firmy Microsoft ma kontroler widoku, jest automatycznie podłączany do wyświetlania monitu po panelu modalnym autoryzacji sesji.

  3. Postępuj zgodnie z instrukcjami znajdującymi się w panelu po prawej stronie aplikacji AppMenu. Powinien zostać wyświetlony komunikat podobny do następującego: Illustration that shows the instruction panel that appears to the right of the AppMenu.

    Po wprowadzeniu podanego kodu na urządzeniu pomocniczym (lub przeglądarce na tym samym urządzeniu) i zalogowaniu się przy użyciu poświadczeń zostanie zwrócony token dostępu do żądanej aplikacji, w tym przypadku Edytor aparatu Unity.

Po tym etapie wszystko w aplikacji powinno być kontynuowane normalnie. Sprawdź konsolę aparatu Unity pod kątem błędów, jeśli nie przechodzisz przez etapy zgodnie z oczekiwaniami.

Kompilowanie na urządzeniu

Jeśli tworzysz aplikację przy użyciu biblioteki MSAL na urządzeniu, musisz dołączyć plik do folderu Assets projektu. Pomaga to kompilatorowi poprawnie skompilować aplikację przy użyciu biblioteki Microsoft.Identity.Client.dll zawartej w temacie Zasoby samouczka.

  1. Dodawanie nowego pliku w obszarze Assets o nazwie link.xml

  2. Dodaj do pliku następujące polecenie:

    <linker>
        <assembly fullname="Microsoft.Identity.Client" preserve="all"/>
        <assembly fullname="System.Runtime.Serialization" preserve="all"/>
        <assembly fullname="System.Core">
            <type fullname="System.Linq.Expressions.Interpreter.LightLambda" preserve="all" />
        </assembly>
    </linker>
    
  3. Zapisz zmiany

Wykonaj kroki opisane w przewodniku Szybki start: wdrażanie przykładu aparatu Unity na urządzeniu HoloLens — kompilowanie przykładowego projektu w celu skompilowania na urządzeniu HoloLens.

Następne kroki

Pozostała część tego zestawu samouczków zawiera artykuły koncepcyjne dotyczące tworzenia aplikacji gotowej do produkcji, która korzysta z usługi Azure Remote Rendering.