Självstudie: Skydda Azure Remote Rendering och modelllagring

I den här självstudien lär du dig att:

  • Skydda Azure Blob Storage som innehåller Azure Remote Rendering-modeller
  • Autentisera med Microsoft Entra-ID för att få åtkomst till din Azure Remote Rendering-instans
  • Använda Azure-autentiseringsuppgifter för Azure Remote Rendering-autentisering

Förutsättningar

Därför behövs extra säkerhet

Det aktuella tillståndet för programmet och dess åtkomst till dina Azure-resurser ser ut så här:

Initial security

Både "AccountID + AccountKey" och "URL + SAS Token" lagrar båda i princip ett användarnamn och lösenord tillsammans. Om till exempel "AccountID + AccountKey" exponerades skulle det vara trivialt för en angripare att använda dina ARR-resurser utan din behörighet på din bekostnad.

Skydda ditt innehåll i Azure Blob Storage

Azure Remote Rendering kan på ett säkert sätt komma åt innehållet i Azure Blob Storage med rätt konfiguration. Se Anvisningar: Länka lagringskonton för att konfigurera din Azure Remote Rendering-instans med dina bloblagringskonton.

När du använder en länkad bloblagring använder du lite olika metoder för att läsa in modeller:

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

Ovanstående rader använder FromSas versionen av params- och sessionsåtgärden. De måste konverteras till icke-SAS-versioner:

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

Nu ska vi ändra RemoteRenderingCoordinator för att läsa in en anpassad modell från ett länkat bloblagringskonto.

  1. Om du inte redan har gjort det slutför du instruktioner: Länka lagringskonton för att ge ARR-instansen behörighet att komma åt bloblagringsinstansen.

  2. Lägg till följande ändrade LoadModel-metod i RemoteRenderingCoordinator strax under den aktuella LoadModel-metoden :

    /// <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;
    }
    

    Den här koden är identisk med den ursprungliga LoadModel metoden, men vi har ersatt SAS-versionen av metodanropen med icke-SAS-versionerna.

    De extra indata storageAccountName och blobName har också lagts till i argumenten. Vi kallar den här nya LoadModel-metoden från en annan metod som liknar den första LoadTestModel-metoden som vi skapade i den första självstudien.

  3. Lägg till följande metod i RemoteRenderingCoordinator strax efter 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)}%"));
    }
    

    Den här koden lägger till tre extra strängvariabler till din RemoteRenderingCoordinator-komponent . Screenshot that highlights the Storage Account Name, Blob Container Name, and Model Path of the RemoteRenderingCoordinator component.

  4. Lägg till dina värden i RemoteRenderingCoordinator-komponenten . Efter att ha följt snabbstarten för modellkonvertering bör dina värden vara:

    • Lagringskontonamn: Ditt lagringskontonamn, det globalt unika namn som du väljer för ditt lagringskonto. I snabbstarten var det här arrtutorialstorage, ditt värde är annorlunda.
    • Namn på blobcontainer: arroutput, bloblagringscontainern
    • Modellsökväg: Kombinationen av "outputFolderPath" och "outputAssetFileName" som definierats i filen arrconfig.json . I snabbstarten var detta "outputFolderPath":"converted/robot", "outputAssetFileName": "robot.arrAsset". Vilket skulle resultera i ett modellsökvägsvärde för "konverterad/robot/robot.arrAsset" är ditt värde annorlunda.

    Dricks

    Om du kör skriptet Conversion.ps1 utan argumentet "-UseContainerSas" matar skriptet ut alla ovanstående värden för din i stället för SAS-token. Linked Model

  5. För tillfället tar du bort eller inaktiverar GameObject TestModel för att göra plats för din anpassade modell att läsas in.

  6. Spela upp scenen och anslut till en fjärrsession.

  7. Öppna snabbmenyn på RemoteRenderingCoordinator och välj Läs in länkad anpassad modell. Load linked model

De här stegen har ökat säkerheten för programmet genom att ta bort SAS-token från det lokala programmet.

Nu ser programmets aktuella tillstånd och dess åtkomst till dina Azure-resurser ut så här:

Better security

Vi har ytterligare ett "lösenord", AccountKey, att ta bort från det lokala programmet. Detta kan göras med Hjälp av Microsoft Entra-autentisering.

Microsoft Entra-autentisering

Med Microsoft Entra-autentisering kan du avgöra vilka personer eller grupper som använder ARR på ett mer kontrollerat sätt. ARR har inbyggt stöd för att acceptera åtkomsttoken i stället för att använda en kontonyckel. Du kan se åtkomsttoken som en tidsbegränsad, användarspecifik nyckel som bara låser upp vissa delar av den specifika resurs som begärdes för.

Skriptet RemoteRenderingCoordinator har ett ombud med namnet ARRCredentialGetter, som innehåller en metod som returnerar ett SessionConfiguration-objekt som används för att konfigurera fjärrsessionshanteringen. Vi kan tilldela en annan metod till ARRCredentialGetter, så att vi kan använda ett Azure-inloggningsflöde och generera ett SessionConfiguration-objekt som innehåller en Azure-åtkomsttoken. Den här åtkomsttoken är specifik för den användare som loggar in.

  1. Följ anvisningar : Konfigurera autentisering – autentisering för distribuerade program, vilket innebär att registrera ett nytt Microsoft Entra-program och konfigurera åtkomst till din ARR-instans.

  2. När du har konfigurerat det nya Microsoft Entra-programmet kontrollerar du att ditt Microsoft Entra-program ser ut som följande bilder:

    Microsoft Entra-program –> autentiseringApp authentication

    Microsoft Entra-program –> API-behörigheterApp APIs

  3. När du har konfigurerat fjärrrenderingskontot kontrollerar du att konfigurationen ser ut som följande bild:

    ARR –> AccessControl (IAM)ARR Role

    Kommentar

    En ägarroll räcker inte för att hantera sessioner via klientprogrammet. För varje användare som du vill ge möjlighet att hantera sessioner måste du ange rollen Fjärrrenderingsklient. För varje användare som du vill hantera sessioner och konvertera modeller måste du ange rollen Fjärrrenderingsadministratör.

Med Azure-sidan av saker på plats måste vi nu ändra hur din kod ansluter till ARR-tjänsten. Det gör vi genom att implementera en instans av BaseARRAuthentication, som returnerar ett nytt SessionConfiguration-objekt . I det här fallet konfigureras kontoinformationen med Azure-åtkomsttoken.

  1. Skapa ett nytt skript med namnet AADAuthentication och ersätt koden med följande:

    // 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;
        }
    }
    

Kommentar

Den här koden är inte på något sätt fullständig och är inte redo för ett kommersiellt program. Du kanske till exempel vill lägga till möjligheten att logga ut. Detta kan göras med hjälp av den Task RemoveAsync(IAccount account) metod som tillhandahålls av klientprogrammet. Den här koden är endast avsedd för självstudier. Implementeringen är specifik för ditt program.

Koden försöker först hämta token tyst med hjälp av AquireTokenSilent. Detta lyckas om användaren tidigare har autentiserat det här programmet. Om det inte lyckas går du vidare till en mer användaransluten strategi.

För den här koden använder vi enhetskodflödet för att hämta en åtkomsttoken. Med det här flödet kan användaren logga in på sitt Azure-konto på en dator eller mobil enhet och få den resulterande token skickad tillbaka till HoloLens-programmet.

Den viktigaste delen av den här klassen ur ett ARR-perspektiv är den här raden:

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

Här skapar vi ett nytt SessionConfiguration-objekt med hjälp av fjärrrenderingsdomänen, konto-ID, kontodomän och åtkomsttoken. Den här token används sedan av ARR-tjänsten för att fråga, skapa och ansluta fjärrrenderingssessioner så länge användaren är auktoriserad baserat på de rollbaserade behörigheter som konfigurerades tidigare.

Med den här ändringen ser programmets aktuella tillstånd och dess åtkomst till dina Azure-resurser ut så här:

Even better security

Eftersom användarautentiseringsuppgifterna inte lagras på enheten (eller i det här fallet ens anges på enheten) är exponeringsrisken låg. Nu använder enheten en användarspecifik, tidsbegränsad åtkomsttoken för åtkomst till ARR, som använder åtkomstkontroll (IAM) för att få åtkomst till Blob Storage. Dessa två steg har tagit bort "lösenorden" från källkoden och ökat säkerheten avsevärt. Detta är dock inte den mest tillgängliga säkerheten. Om du flyttar modellen och sessionshanteringen till en webbtjänst förbättras säkerheten ytterligare. Extra säkerhetsöverväganden beskrivs i kapitlet Kommersiell beredskap .

Testa Microsoft Entra-autentisering

När Microsoft Entra-autentisering är aktiv i Unity-redigeraren måste du autentisera varje gång du startar programmet. På enheten sker autentiseringssteget första gången och krävs bara igen när token upphör att gälla eller ogiltigförklaras.

  1. Lägg till Microsoft Entra-autentiseringskomponenten i RemoteRenderingCoordinator GameObject.

    Microsoft Entra auth component

Kommentar

Om du använder det slutförda projektet från ARR-exempellagringsplatsen måste du aktivera Microsoft Entra-autentiseringskomponenten genom att klicka på kryssrutan bredvid dess rubrik.

  1. Fyll i dina värden för klient-ID:t och klient-ID:t. Dessa värden finns på appregistreringens översiktssida:

    • Klient-ID för Active Directory-program är det program-ID (klient-ID ) som finns i din Microsoft Entra-appregistrering (se bild nedan).
    • Azure-klientorganisations-ID är katalog-ID:t (klientorganisation) som finns i din Microsoft Entra-appregistrering (se bild nedan).
    • Azure Remote Rendering Domain är samma domän som du har använt i RemoteRenderingCoordinators fjärrrenderingsdomän.
    • Azure Remote Rendering-konto-ID är samma konto-ID som du har använt för RemoteRenderingCoordinator.
    • Azure Remote Rendering Account Domain är samma kontodomän som du har använt i RemoteRenderingCoordinator.

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

  2. Tryck på Spela upp i Unity-redigeraren och godkänn att en session körs. Eftersom Microsoft Entra-autentiseringskomponenten har en vystyrenhet ansluts den automatiskt för att visa en uppmaning efter sessionsauktoriseringsmodalpanelen.

  3. Följ anvisningarna i panelen till höger om AppMenu. Du bör se något som liknar detta: Illustration that shows the instruction panel that appears to the right of the AppMenu.

    När du har angett den angivna koden på den sekundära enheten (eller webbläsaren på samma enhet) och loggat in med dina autentiseringsuppgifter, returneras en åtkomsttoken till det begärande programmet, i det här fallet Unity-redigeraren.

Efter detta bör allt i programmet fortsätta normalt. Kontrollera om det finns fel i Unity-konsolen om du inte går igenom stegen som förväntat.

Skapa till enhet

Om du skapar ett program som använder MSAL till enheten måste du inkludera en fil i projektets resursmapp. Detta hjälper kompilatorn att skapa programmet korrekt med hjälp av Microsoft.Identity.Client.dll som ingår i självstudietillgångarna.

  1. Lägg till en ny fil i Tillgångar med namnet link.xml

  2. Lägg till följande för i filen:

    <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. Spara ändringarna

Följ stegen i Snabbstart: Distribuera Unity-exempel till HoloLens – Skapa exempelprojektet för att skapa till HoloLens.

Nästa steg

Resten av den här självstudieuppsättningen innehåller konceptuella artiklar för att skapa ett produktionsklart program som använder Azure Remote Rendering.