Краткое руководство. Инициализация клиентских приложений (C++)

В этом кратком руководстве показано, как реализовать шаблон инициализации клиентов, используемый пакетом SDK MIP для C++ во время выполнения.

Примечание.

Действия, описанные в этом кратком руководстве, применимы к любому клиентскому приложению, использующему пакеты SDK MIP File, Policy и Protection. Хотя в этом кратком руководстве показано использование пакетов SDK File, этот же шаблон применим к клиентам, использующим пакеты SDK Policy и Protection. Выполняйте оставшиеся краткие руководства последовательно, так как каждое из них основано на предыдущем, и это первое.

Необходимые компоненты

Обязательно сделайте следующее, если еще этого не сделали:

Создание решения и проекта Visual Studio

Сначала мы создадим и настроим первоначальное решение и проект Visual Studio для работы с другими краткими руководствами.

  1. В Visual Studio 2017 откройте меню Файл и выберите Создать и Проект. В диалоговом окне Новый проект:

    • В области слева выберите Установленные продукты, Другие языки и Visual C++.

    • В области в центре выберите Консольное приложение Windows.

    • В области внизу обновите значения полей Имя и Расположение для проекта, а также Имя решения.

    • Завершив, щелкните ОК внизу справа.

      Visual Studio solution creation

  2. Добавьте пакет Nuget для пакета SDK MIP File в проект:

    • В Обозревателе решений щелкните правой кнопкой мыши узел проекта (непосредственно под верхним узлом или узлом решения) и выберите Управление пакетами NuGet:

    • Когда откроется вкладка Диспетчер пакетов NuGet в области вкладок группы редакторов:

      • Выберите Обзор.
      • Введите Microsoft.InformationProtection в поле поиска.
      • Выберите пакет Microsoft.InformationProtection.File.
      • Когда отобразится диалоговое окно подтверждения Предварительный просмотр изменений, щелкните "Установить" и "ОК".

      Visual Studio add NuGet package

Реализация класса наблюдателя для отслеживания объектов профиля и подсистемы File

Теперь создадим базовую реализацию для класса наблюдателя профиля File, расширив класс mip::FileProfile::Observer пакета SDK. Экземпляр наблюдателя создается и используется позднее для отслеживания загрузки объекта профиля File и добавления объекта подсистемы в профиль.

  1. Добавьте в проект новый класс, который создает файлы header/.h и implementation/.cpp:

    • В Обозревателе решений снова щелкните правой кнопкой мыши узел проекта, а затем выберите Добавить и Класс.

    • В диалоговом окне Добавить класс:

      • В поле Имя класса введите profile_observer. Обратите внимание, что поля файлов .h и .cpp заполняются автоматически в зависимости от имени, которое вы вводите.
      • По завершении щелкните ОК.

      Visual Studio add class

  2. После создания файлов .h и .cpp для класса оба файла откроются на вкладках группы редакторов. Теперь обновите каждый файл, чтобы реализовать новый класс наблюдателя:

    • Обновите profile_observer.h, выбрав и удалив созданный класс profile_observer. Не удаляйте директивы препроцессора, созданные на предыдущем этапе (#pragma, #include). Затем скопируйте и вставьте в файл следующий исходный код после любых существующих директив препроцессора:

      #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;
      };
      
    • Обновите profile_observer.cpp, выбрав и удалив созданную реализацию класса profile_observer. Не удаляйте директивы препроцессора, созданные на предыдущем этапе (#pragma, #include). Затем скопируйте и вставьте в файл следующий исходный код после любых существующих директив препроцессора:

      #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. Вы можете нажать клавишу F6 (Выполнить сборку решения) для запуска тестовой компиляции решения или переходу по ссылке на него, чтобы убедиться, что его сборка успешно выполняется, прежде чем продолжить.

Реализация делегата проверки подлинности

Пакет SDK MIP реализует проверку подлинности с использованием расширяемости класса, которая предоставляет механизм совместного использования проверки подлинности с клиентским приложением. Клиент должен получить подходящий маркер доступа OAuth2 и предоставить его пакету SDK MIP во время выполнения.

Теперь создайте реализацию для делегата проверки подлинности, расширив класс mip::AuthDelegate пакета SDK и переопределив или реализовав чистую виртуальную функцию mip::AuthDelegate::AcquireOAuth2Token(). Экземпляр делегата проверки подлинности создается и используется позже объектами профиля и подсистемы File.

  1. Добавьте в проект еще один класс с помощью той же функции "Добавить класс" в Visual Studio, которая использовалась на шаге 1 предыдущего раздела. В этот раз введите auth_delegate в поле Имя класса.

  2. Теперь обновите каждый файл, чтобы реализовать новый класс делегата проверки подлинности:

    • Обновите auth_delegate.h, изменив весь созданный код класса auth_delegate на приведенный ниже исходный код. Не удаляйте директивы препроцессора, созданные на предыдущем этапе (#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;
      };
      
    • Обновите auth_delegate.cpp, изменив всю созданную реализацию класса auth_delegate на приведенный ниже исходный код. Не удаляйте директивы препроцессора, созданные на предыдущем этапе (#pragma, #include).

      Важно!

      Следующий код получения маркеров не подходит для использования в рабочих средах. В рабочей среде его необходимо заменить кодом, который динамически получает маркер, используя следующее:

      • Идентификатор приложения и универсальный код ресурса (URI ответа и перенаправления), указанный в регистрации приложения Microsoft Entra (URI ответа или перенаправления должен соответствовать регистрации приложения).
      • URL-адрес ресурса и центра, переданный пакетом SDK в аргументе challenge (URL-адрес ресурса должен соответствовать API или разрешениям регистрации приложения).
      • Действительные учетные данные приложения или пользователя, в которых учетная запись совпадает с аргументом identity, переданным пакетом SDK. Собственные клиенты OAuth2 должны запрашивать учетные данные пользователей и использовать поток кода авторизации. Конфиденциальные клиенты OAuth2 могут использовать свои безопасные учетные данные с потоком учетных данных клиента (например, служба) или запрашивать учетные данные пользователя с помощью потока кода авторизации (например, веб-приложение).

      Получение маркера OAuth2 — это сложный протокол, который обычно выполняется с помощью библиотеки. При необходимости TokenAcquireOAuth2Token() вызывается только пакетом SDK MIP.

      #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. Вы можете нажать клавишу F6 (Выполнить сборку решения) для запуска тестовой компиляции решения или переходу по ссылке на него, чтобы убедиться, что его сборка успешно выполняется, прежде чем продолжить.

Теперь создайте реализацию для делегата согласия, расширив класс mip::ConsentDelegate пакета SDK и переопределив или реализовав чистую виртуальную функцию mip::AuthDelegate::GetUserConsent(). Экземпляр делегата согласия создается и используется позже объектами профиля и подсистемы File.

  1. Добавьте в проект еще один класс с помощью той же функции "Добавить класс" в Visual Studio, которая использовалась ранее. В этот раз введите consent_delegate в поле Имя класса.

  2. Теперь обновите каждый файл, чтобы реализовать новый класс делегата согласия:

    • Обновите consent_delegate.h, изменив весь созданный код класса consent_delegate на приведенный ниже исходный код. Не удаляйте директивы препроцессора, созданные на предыдущем этапе (#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;
      };
      
    • Обновите consent_delegate.cpp, изменив всю созданную реализацию класса consent_delegate на приведенный ниже исходный код. Не удаляйте директивы препроцессора, созданные на предыдущем этапе (#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. Вы можете нажать клавишу F6 (Выполнить сборку решения) для запуска тестовой компиляции решения или переходу по ссылке на него, чтобы убедиться, что его сборка успешно выполняется, прежде чем продолжить.

Создание профиля и подсистемы File

Как уже упоминалось, для клиентов пакета SDK, использующих API MIP, необходимы объекты профиля и подсистемы. Выполните часть этого краткого руководства, добавив код для создания экземпляров объектов профиля и подсистемы:

  1. В Обозревателе решений откройте файл .cpp в проекте, содержащем реализацию метода main(). По умолчанию он имеет то же имя, что и содержащий его проект, который вы указали при создании проекта.

  2. Удалите созданную реализацию main(). Не удаляйте директивы препроцессора, созданные Visual Studio при создании проекта (#pragma, #include). Добавьте следующий код после директив препроцессора:

#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. Замените все заполнители в только что вставленном вами исходном коде строковыми константами:

    Заполнитель Значение Пример
    <application-id> Идентификатор приложения Microsoft Entra (GUID), назначенный приложению, зарегистрированным на шаге 2 статьи "Настройка и настройка пакета SDK MIP". Замените два экземпляра. "0edbblll-8773-44de-b87c-b8c6276d41eb"
    <application-name> Определенное пользователем понятное имя вашего приложения. Должен содержать допустимые символы ASCII (за исключением ";") и в идеале соответствует имени приложения, используемому в регистрации Microsoft Entra. "AppInitialization"
    <application-version> Определенные пользователем сведения о версии вашего приложения. Должны содержать допустимые символы ASCII (за исключением ;). "1.1.0.0"
    <engine-account> Учетная запись, используемая для удостоверения подсистемы. При проверке подлинности с помощью учетной записи пользователя во время получения маркера она должна соответствовать этому значению. "user1@tenant.onmicrosoft.com"
    <engine-state> Определяемое пользователем состояние, связанное с подсистемой. "My App State"
  2. Теперь вы можете выполнить окончательную сборку приложения и устранить все ошибки. Ваш код должен быть успешно скомпилирован, но не будет работать правильно, пока вы не завершите следующее краткое руководство. При запуске приложения вы увидите результат, аналогичный указанному ниже. У вас не будет маркера доступа, который нужно предоставить, пока вы не завершите следующее краткое руководство.

Дальнейшие действия

Теперь, когда код инициализации написан, вы можете переходить к следующему краткому руководству по началу работы с пакетом SDK MIP File.