Guida introduttiva: Inizializzazione dell'applicazione client (C++)

Questa guida introduttiva illustra come implementare il modello di inizializzazione client, usato da MIP C++ SDK in fase di esecuzione.

Nota

I passaggi descritti in questa guida introduttiva sono necessari per qualsiasi applicazione client che usa gli SDK di file, criteri o protezione MIP. Anche se questa guida introduttiva illustra l'uso degli SDK di file, questo stesso modello è applicabile ai client che usano gli SDK per criteri e protezione. Completare le guide di avvio rapido rimanenti in modo seriale, perché ognuna si basa su quella precedente, con questa prima.

Prerequisiti

Se non è già stato fatto, assicurarsi di:

  • Completare i passaggi della configurazione e della configurazione di Microsoft Information Protection SDK (MIP). Questa guida introduttiva all'inizializzazione dell'applicazione client si basa sull'installazione e la configurazione appropriate dell'SDK.
  • Facoltativamente:
    • Esaminare gli oggetti profilo e motore. I profili e gli oggetti motore sono concetti universali, richiesti dai client che usano gli SDK di protezione/file/criteri MIP.
    • Vedere Concetti relativi all'autenticazione per informazioni sull'implementazione dell'autenticazione e del consenso da parte dell'SDK e dell'applicazione client.
    • Esaminare i concetti di Observer per altre informazioni sugli osservatori e su come vengono implementati. MIP SDK usa il modello observer per implementare le notifiche degli eventi asincrone.

Creare una soluzione e un progetto di Visual Studio

Prima di tutto si crea e si configura la soluzione e il progetto iniziale di Visual Studio, in cui vengono compilate le altre guide introduttive.

  1. Aprire Visual Studio 2017, selezionare il menu File , Nuovo, Progetto. Nella finestra di dialogo Nuovo progetto :

    • Nel riquadro sinistro, in Installato, Altri linguaggi selezionare Visual C++.

    • Nel riquadro centrale selezionare Applicazione console di Windows

    • Nel riquadro inferiore aggiornare di conseguenza il nome del progetto, il percorso e il nome della soluzione che lo contiene.

    • Al termine, fare clic sul pulsante OK in basso a destra.

      Visual Studio solution creation

  2. Aggiungere il pacchetto Nuget per MIP File SDK al progetto:

    • Nella Esplora soluzioni fare clic con il pulsante destro del mouse sul nodo del progetto (direttamente sotto il nodo superiore/soluzione) e selezionare Gestisci pacchetti NuGet...:

    • Quando si apre la scheda Gestione pacchetti NuGet nell'area Schede Gruppo editor:

      • Selezionare Sfoglia.
      • Immettere "Microsoft.InformationProtection" nella casella di ricerca.
      • Selezionare il pacchetto "Microsoft.InformationProtection.File".
      • Fare clic su "Installa", quindi su "OK" quando viene visualizzata la finestra di dialogo di conferma delle modifiche dell'anteprima.

      Visual Studio add NuGet package

Implementare una classe observer per monitorare gli oggetti profilo file e motore

Creare ora un'implementazione di base per una classe osservatore del profilo file estendendo la classe dell'SDK mip::FileProfile::Observer . L'osservatore viene creata un'istanza e usata in un secondo momento, per monitorare il caricamento dell'oggetto Profilo file e aggiungere l'oggetto motore al profilo.

  1. Aggiungere una nuova classe al progetto, che genera automaticamente i file header/.h e implementation/.cpp:

    • Nel Esplora soluzioni fare di nuovo clic con il pulsante destro del mouse sul nodo del progetto, selezionare Aggiungi e quindi selezionare Classe.

    • Nella finestra di dialogo Aggiungi classe :

      • Nel campo Nome classe immettere "profile_observer". Si noti che i campi del file con estensione h e cpp vengono popolati automaticamente in base al nome immesso.
      • Al termine, fare clic sul pulsante OK .

      Visual Studio add class

  2. Dopo aver generato i file con estensione h e cpp per la classe , entrambi i file vengono aperti nelle schede Gruppo editor. Aggiornare ora ogni file per implementare la nuova classe observer:

    • Aggiornare "profile_observer.h" selezionando/eliminando la classe generata profile_observer . Non rimuovere le direttive del preprocessore generate dal passaggio precedente (#pragma, #include). Copiare/incollare quindi l'origine seguente nel file, dopo eventuali direttive del preprocessore esistenti:

      #include <memory>
      #include "mip/file/file_profile.h"
      
      class ProfileObserver final : public mip::FileProfile::Observer {
      public:
           ProfileObserver() { }
           void OnLoadSuccess(const std::shared_ptr<mip::FileProfile>& profile, const std::shared_ptr<void>& context) override;
           void OnLoadFailure(const std::exception_ptr& error, const std::shared_ptr<void>& context) override;
           void OnAddEngineSuccess(const std::shared_ptr<mip::FileEngine>& engine, const std::shared_ptr<void>& context) override;
           void OnAddEngineFailure(const std::exception_ptr& error, const std::shared_ptr<void>& context) override;
      };
      
    • Aggiornare "profile_observer.cpp" selezionando/eliminando l'implementazione della classe generata profile_observer . Non rimuovere le direttive del preprocessore generate dal passaggio precedente (#pragma, #include). Copiare/incollare quindi l'origine seguente nel file, dopo eventuali direttive del preprocessore esistenti:

      #include <future>
      
      using std::promise;
      using std::shared_ptr;
      using std::static_pointer_cast;
      using mip::FileEngine;
      using mip::FileProfile;
      
      void ProfileObserver::OnLoadSuccess(const shared_ptr<FileProfile>& profile, const shared_ptr<void>& context) {
           auto promise = static_pointer_cast<std::promise<shared_ptr<FileProfile>>>(context);
           promise->set_value(profile);
      }
      
      void ProfileObserver::OnLoadFailure(const std::exception_ptr& error, const shared_ptr<void>& context) {
           auto promise = static_pointer_cast<std::promise<shared_ptr<FileProfile>>>(context);
           promise->set_exception(error);
      }
      
      void ProfileObserver::OnAddEngineSuccess(const shared_ptr<FileEngine>& engine, const shared_ptr<void>& context) {
           auto promise = static_pointer_cast<std::promise<shared_ptr<FileEngine>>>(context);
           promise->set_value(engine);
      }
      
      void ProfileObserver::OnAddEngineFailure(const std::exception_ptr& error, const shared_ptr<void>& context) {
           auto promise = static_pointer_cast<std::promise<shared_ptr<FileEngine>>>(context);
           promise->set_exception(error);
      }
      
  3. Facoltativamente, usare F6 (Compila soluzione) per eseguire una compilazione/collegamento di test della soluzione, per assicurarsi che venga compilata correttamente prima di continuare.

Implementare un delegato di autenticazione

MIP SDK implementa l'autenticazione usando l'estendibilità della classe, che fornisce un meccanismo per condividere l'autenticazione con l'applicazione client. Il client deve acquisire un token di accesso OAuth2 appropriato e fornire all'SDK MIP in fase di esecuzione.

Creare ora un'implementazione per un delegato di autenticazione estendendo la classe dell'SDK mip::AuthDelegate ed eseguendo l'override/implementazione della mip::AuthDelegate::AcquireOAuth2Token() funzione virtuale pura. Il delegato di autenticazione viene creata un'istanza e usata in un secondo momento dagli oggetti Profilo file e Motore file.

  1. Usando la stessa funzionalità "Aggiungi classe" di Visual Studio usata nel passaggio 1 della sezione precedente, aggiungere un'altra classe al progetto. Questa volta immettere "auth_delegate" nel campo Nome classe.

  2. Aggiornare ora ogni file per implementare la nuova classe delegato di autenticazione:

    • Aggiornare "auth_delegate.h", sostituendo tutto il codice di classe generato auth_delegate con l'origine seguente. Non rimuovere le direttive del preprocessore generate dal passaggio precedente (#pragma, #include):

      #include <string>
      #include "mip/common_types.h"
      
      class AuthDelegateImpl final : public mip::AuthDelegate {
      public:
           AuthDelegateImpl() = delete;        // Prevents default constructor
      
           AuthDelegateImpl(
             const std::string& appId)         // AppID for registered AAD app
             : mAppId(appId) {};
      
           bool AcquireOAuth2Token(            // Called by MIP SDK to get a token
             const mip::Identity& identity,    // Identity of the account to be authenticated, if known
             const OAuth2Challenge& challenge, // Authority (AAD tenant issuing token), and resource (API being accessed; "aud" claim).
             OAuth2Token& token) override;     // Token handed back to MIP SDK
      
      private:
           std::string mAppId;
           std::string mToken;
           std::string mAuthority;
           std::string mResource;
      };
      
    • Aggiornare "auth_delegate.cpp", sostituendo tutta l'implementazione della classe generata auth_delegate con l'origine seguente. Non rimuovere le direttive del preprocessore generate dal passaggio precedente (#pragma, #include).

      Importante

      Il codice di acquisizione del token seguente non è adatto per l'uso in produzione. Nell'ambiente di produzione, questa operazione deve essere sostituita dal codice che acquisisce dinamicamente un token usando:

      • L'URI appId e reply/redirect specificato nella registrazione dell'app Microsoft Entra (l'URI di risposta/reindirizzamento deve corrispondere alla registrazione dell'app)
      • L'autorità e l'URL della risorsa passati dall'SDK nell'argomento challenge (l'URL della risorsa deve corrispondere all'API/autorizzazioni della registrazione dell'app)
      • Credenziali valide per app/utente, in cui l'account corrisponde all'argomento identity passato dall'SDK. I client "nativi" OAuth2 devono richiedere le credenziali utente e usare il flusso "codice di autorizzazione". I "client riservati" OAuth2 possono usare le proprie credenziali sicure con il flusso "credenziali client" (ad esempio un servizio) o richiedere le credenziali utente usando il flusso "codice di autorizzazione", ad esempio un'app Web.

      L'acquisizione di token OAuth2 è un protocollo complesso e normalmente viene eseguita usando una libreria. TokenAcquireOAuth2Token() viene chiamato solo dall'SDK MIP, come richiesto.

      #include <iostream>
      using std::cout;
      using std::cin;
      using std::string;
      
      bool AuthDelegateImpl::AcquireOAuth2Token(const mip::Identity& identity, const OAuth2Challenge& challenge, OAuth2Token& token) 
      {
           // Acquire a token manually, reuse previous token if same authority/resource. In production, replace with token acquisition code.
           string authority = challenge.GetAuthority();
           string resource = challenge.GetResource();
           if (mToken == "" || (authority != mAuthority || resource != mResource))
           {
               cout << "\nRun the PowerShell script to generate an access token using the following values, then copy/paste it below:\n";
               cout << "Set $authority to: " + authority + "\n";
               cout << "Set $resourceUrl to: " + resource + "\n";
               cout << "Sign in with user account: " + identity.GetEmail() + "\n";
               cout << "Enter access token: ";
               cin >> mToken;
               mAuthority = authority;
               mResource = resource;
               system("pause");
           }
      
           // Pass access token back to MIP SDK
           token.SetAccessToken(mToken);
      
           // True = successful token acquisition; False = failure
           return true;
      }
      
  3. Facoltativamente, usare F6 (Compila soluzione) per eseguire una compilazione/collegamento di test della soluzione, per assicurarsi che venga compilata correttamente prima di continuare.

Creare ora un'implementazione per un delegato di consenso estendendo la classe dell'SDK mip::ConsentDelegate ed eseguendo l'override/implementazione della mip::AuthDelegate::GetUserConsent() funzione virtuale pura. Il delegato di consenso viene creata un'istanza e usata in un secondo momento dagli oggetti Profilo file e Motore file.

  1. Usando la stessa funzionalità "Aggiungi classe" di Visual Studio usata in precedenza, aggiungere un'altra classe al progetto. Questa volta immettere "consent_delegate" nel campo Nome classe.

  2. Aggiornare ora ogni file per implementare la nuova classe delegato di consenso:

    • Aggiornare "consent_delegate.h", sostituendo tutto il codice di classe generato consent_delegate con l'origine seguente. Non rimuovere le direttive del preprocessore generate dal passaggio precedente (#pragma, #include):

      #include "mip/common_types.h"
      #include <string>
      
      class ConsentDelegateImpl final : public mip::ConsentDelegate {
      public:
           ConsentDelegateImpl() = default;
           virtual mip::Consent GetUserConsent(const std::string& url) override;
      };
      
    • Aggiornare "consent_delegate.cpp", sostituendo tutte le implementazioni della classe generata consent_delegate con l'origine seguente. Non rimuovere le direttive del preprocessore generate dal passaggio precedente (#pragma, #include).

      #include <iostream>
      using mip::Consent;
      using std::string;
      
      Consent ConsentDelegateImpl::GetUserConsent(const string& url) 
      {
           // Accept the consent to connect to the url
           std::cout << "SDK will connect to: " << url << std::endl;
           return Consent::AcceptAlways;
      }
      
  3. Facoltativamente, usare F6 (Compila soluzione) per eseguire una compilazione/collegamento di test della soluzione, per assicurarsi che venga compilata correttamente prima di continuare.

Costruire un profilo e un motore di file

Come accennato, gli oggetti profilo e motore sono necessari per i client SDK che usano le API MIP. Completare la parte di scrittura del codice di questa guida introduttiva aggiungendo codice per creare un'istanza degli oggetti profilo e motore:

  1. Da Esplora soluzioni aprire il file con estensione cpp nel progetto che contiene l'implementazione del main() metodo . Per impostazione predefinita, il nome del progetto che lo contiene è stato specificato durante la creazione del progetto.

  2. Rimuovere l'implementazione generata di main(). Non rimuovere le direttive del preprocessore generate da Visual Studio durante la creazione del progetto (#pragma, #include). Aggiungere il codice seguente dopo qualsiasi direttiva del preprocessore:

#include "mip/mip_context.h"  
#include "auth_delegate.h"
#include "consent_delegate.h"
#include "profile_observer.h"

using std::promise;
using std::future;
using std::make_shared;
using std::shared_ptr;
using std::string;
using std::cout;
using mip::ApplicationInfo;
using mip::FileProfile;
using mip::FileEngine;

int main()
{
  // Construct/initialize objects required by the application's profile object
  // ApplicationInfo object (App ID, name, version)
  ApplicationInfo appInfo{"<application-id>",      
                          "<application-name>",
                          "<application-version>"};

  // Create MipConfiguration object.
  std::shared_ptr<mip::MipConfiguration> mipConfiguration = std::make_shared<mip::MipConfiguration>(appInfo,    
				                                                                                               "mip_data", 
                                                                                      			         mip::LogLevel::Trace, 
                                                                                                     false);


  std::shared_ptr<mip::MipContext> mMipContext = mip::MipContext::Create(mipConfiguration);

  auto profileObserver = make_shared<ProfileObserver>();                     // Observer object
  auto authDelegateImpl = make_shared<AuthDelegateImpl>("<application-id>"); // Authentication delegate object (App ID)                 
  auto consentDelegateImpl = make_shared<ConsentDelegateImpl>();             // Consent delegate object

  // Construct/initialize profile object
  FileProfile::Settings profileSettings(
                                mMipContext,
                                mip::CacheStorageType::OnDisk,
                                consentDelegateImpl,
                                profileObserver);

  // Set up promise/future connection for async profile operations; load profile asynchronously
  auto profilePromise = make_shared<promise<shared_ptr<FileProfile>>>();
  auto profileFuture = profilePromise->get_future();

  try
	  { 
		  mip::FileProfile::LoadAsync(profileSettings, profilePromise);
  }
	  catch (const std::exception& e)
	  {
		  cout << "An exception occurred... are the Settings and ApplicationInfo objects populated correctly?\n\n" << e.what() << "'\n";
			
		  system("pause");
		  return 1;
	  }
	  auto profile = profileFuture.get();

  // Construct/initialize engine object
  FileEngine::Settings engineSettings(
                                  mip::Identity("<engine-account>"), // Engine identity (account used for authentication)
                                  authDelegateImpl,		       // Token acquisition implementation
				    "<engine-state>",                  // User-defined engine state
                                  "en-US");                          // Locale (default = en-US)
                                  
  // Set the engineId for caching. 
  engineSettings.SetEngineId("<engine-account>");
  // Set up promise/future connection for async engine operations; add engine to profile asynchronously
  auto enginePromise = make_shared<promise<shared_ptr<FileEngine>>>();
  auto engineFuture = enginePromise->get_future();
  profile->AddEngineAsync(engineSettings, enginePromise);
  std::shared_ptr<FileEngine> engine; 
  try
  {
    engine = engineFuture.get();
  }
  catch (const std::exception& e)
  {
    cout << "An exception occurred... is the access token incorrect/expired?\n\n" << e.what() << "'\n";
     
    system("pause");
    return 1;
  }

  // Application shutdown. Null out profile and engine, call ReleaseAllResources();
  // Application may crash at shutdown if resources aren't properly released.
  // handler = nullptr; // This will be used in later quick starts.
  engine = nullptr;
  profile = nullptr;   
  mMipContext->ShutDown();
  mMipContext = nullptr;

  return 0;
  }
  1. Sostituire tutti i valori segnaposto nel codice sorgente appena incollato, usando costanti stringa:

    Segnaposto Valore Esempio
    <application-id> ID applicazione Microsoft Entra (GUID) assegnato all'applicazione registrata nel passaggio 2 dell'articolo "Configurazione e configurazione di MIP SDK". Sostituire 2 istanze. "0edbblll-8773-44de-b87c-b8c6276d41eb"
    <application-name> Nome descrittivo definito dall'utente per l'applicazione. Deve contenere caratteri ASCII validi (esclusi ';') e idealmente corrisponde al nome dell'applicazione usato nella registrazione di Microsoft Entra. "AppInitialization"
    <application-version> Informazioni sulla versione definite dall'utente per l'applicazione. Deve contenere caratteri ASCII validi (escluso ';'). "1.1.0.0"
    <engine-account> Account usato per l'identità del motore. Quando si esegue l'autenticazione con un account utente durante l'acquisizione del token, deve corrispondere a questo valore. "user1@tenant.onmicrosoft.com"
    <stato del motore> Stato definito dall'utente da associare al motore. "My App State"
  2. A questo punto, eseguire una compilazione finale dell'applicazione e risolvere eventuali errori. Il codice dovrebbe essere compilato correttamente, ma non verrà ancora eseguito correttamente fino a quando non si completa la guida introduttiva successiva. Se si esegue l'applicazione, viene visualizzato un output simile al seguente. Non si avrà un token di accesso da fornire fino a quando non si completa la guida introduttiva successiva.

Passaggi successivi

Ora che il codice di inizializzazione è completo, si è pronti per la guida introduttiva successiva, in cui si inizierà a sperimentare gli SDK di file MIP.