快速入門:用戶端應用程式初始化 (C++)

這個快速入門工具會教你如何實作 MIP C++ SDK 在執行時使用的用戶端初始化模式。

備註

任何使用 MIP 檔案、政策或防護 SDK 的客戶端應用程式,都需要遵循本快速入門中所述的步驟。 雖然這個快速入門說明了檔案 SDK 的使用,但同樣的模式也適用於使用政策與保護 SDK 的客戶端。 請依序完成其餘的快速入門,因為每個快速入門都以前一個為基礎,而這個是第一個。

先決條件

如果您尚未這麼做,請務必:

建立 Visual Studio 方案和專案

首先,建立並設定初始的 Visual Studio 解決方案與專案,其他快速入門工具則在此基礎上建置。

  1. 開啟 Visual Studio 2022 或更新版本,選擇 檔案 選單, 新增專案。 在 [ 新增專案] 對話框中:

    • 在左窗格中的 [ 已安裝] 底下,選取 [ 其他語言],選取 [Visual C++]。

    • 在中央窗格中,選取 [Windows 控制台應用程式]

    • 在底部窗格中,據以更新專案 [名稱]、[ 位置] 和包含 的方案名稱

    • 完成後,按下右下角的 [ 確定] 按鈕。

      Visual Studio 解決方案建立。

  2. 將 MIP 檔案 SDK 的 NuGet 套件新增至您的專案:

    • [方案總管] 中,以滑鼠右鍵按兩下項目節點(直接在頂端/方案節點下),然後選取 [ 管理 NuGet 套件...] :

    • NuGet 套件管理員 索引標籤在 [編輯器群組] 索引標籤區域中開啟時:

      • 選擇瀏覽
      • 在搜尋方塊中輸入 「Microsoft.InformationProtection」。。
      • 選取 「Microsoft.InformationProtection.File」 套件。
      • 按兩下 [安裝],然後在 [預覽變更 確認] 對話框顯示時按兩下 [確定]。

      Visual Studio 新增 NuGet 套件。

實作觀察者類別來監視檔案配置檔和引擎物件

現在透過擴充 SDK 的 mip::FileProfile::Observer 類別,建立檔案設定檔觀察器類別的基本實作。 你之後再實例化並使用 Observer 來監控檔案設定檔物件的載入過程,以及將引擎物件加入設定檔。

  1. 將新類別新增至您的專案,這會為您產生標頭/.h 和實作/.cpp檔案:

    • 方案總管中,再次以滑鼠右鍵按兩下項目節點,選取 [ 新增],然後選取 [ 類別]。

    • 在 [ 新增類別 ] 對話框中:

      • 在 [ 類別名稱] 欄位中,輸入 「profile_observer」。 請注意,根據您輸入的名稱,會自動填入 .h 檔案.cpp檔案 欄位。
      • 完成後,按兩下 [ 確定] 按鈕。

      Visual Studio 新增類別。

  2. 當你產生 .h 和 .cpp 檔案後,Visual Studio 會在編輯器群組分頁中開啟兩個檔案。 現在更新每個檔案以實作新的觀察者類別:

    • 選取/刪除產生的 profile_observer 類別,以更新 “profile_observer.h”。 請勿 移除上一個步驟所產生的預處理器指示詞(#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 類別實作,以更新 “profile_observer.cpp”。 請勿 移除上一個步驟所產生的預處理器指示詞(#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 (建置方案) 來執行解決方案的測試編譯/連結,以確保它成功建置,然後再繼續。

實作驗證代理

MIP SDK 透過類別擴充性實作認證,提供與客戶端應用程式共享認證工作的機制。 用戶端必須取得合適的 OAuth2 存取權杖,並在執行時提供給 MIP SDK。

現在,透過擴展 SDK mip::AuthDelegate 類別並覆寫或實作 mip::AuthDelegate::AcquireOAuth2Token() 純虛擬函式,建立一個認證代理的實作。 File 設定檔和 File 引擎物件稍後會將驗證委派具現化並加以使用。

  1. 使用我們在上一節的步驟 #1 中使用的相同 Visual Studio「新增類別」功能,將另一個類別新增至您的專案。 這次,請在 [類別名稱 ] 字段中輸入 「auth_delegate」。。

  2. 現在更新每個檔案,以實作新的驗證委派類別:

    • 以下列來源取代所有產生的 auth_delegate 類別程序代碼,以更新 「auth_delegate.h」。。 請勿 移除上一個步驟所產生的預處理器指示詞(#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 類別實作,以更新 「auth_delegate.cpp」。。 請勿 移除上一個步驟所產生的預處理器指示詞(#pragma、#include)。

      這很重要

      以下代幣取得代碼不適合生產使用。 在生產環境中,請將其替換為使用以下方式動態取得權杖的程式碼:

      • Microsoft Entra 應用程式註冊中指定的 appId 和回復/重新導向 URI(回復/重新導向 URI 必須 符合您的應用程式註冊)
      • SDK 在自變數中 challenge 傳遞的授權單位和資源 URL(資源 URL 必須 符合您應用程式註冊的 API/許可權)
      • 有效的應用程式/用戶認證,其中帳戶符合 identity SDK 所傳遞的自變數。 OAuth2「原生」客戶端應該提示輸入用戶認證,並使用「授權碼」流程。 OAuth2「機密用戶端」可以使用自己的安全認證搭配「客戶端認證」流程(例如服務),或使用「授權碼」流程提示使用者認證(例如 Web 應用程式)。

      OAuth2 代幣取得是一項複雜的協議,通常透過函式庫來完成。 TokenAcquireOAuth2Token() 只會 視需要由 MIP SDK 呼叫。

      #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 (建置方案) 來執行解決方案的測試編譯/連結,以確保它成功建置,然後再繼續。

接著透過擴展 SDK mip::ConsentDelegate 類別並覆寫或實作 mip::AuthDelegate::GetUserConsent() 純虛擬函式,建立同意代理的實作。 檔案設定檔與檔案引擎物件會在之後實例化並使用同意代理。

  1. 使用我們先前使用的相同 Visual Studio「新增類別」功能,將另一個類別新增至您的專案。 這次,在 [類別名稱 ] 字段中輸入 「consent_delegate」。。

  2. 現在更新每個檔案,以實作新的同意委派類別:

    • 將所有產生的 consent_delegate 類別程式碼以下列來源取代,以更新「consent_delegate.h」。 請勿 移除上一個步驟所產生的預處理器指示詞(#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 類別實作,以更新 「consent_delegate.cpp」。。 請勿 移除上一個步驟所產生的預處理器指示詞(#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 (建置方案) 來執行解決方案的測試編譯/連結,以確保它成功建置,然後再繼續。

建構檔案概要和引擎

如前所述,使用 MIP API 的 SDK 用戶端需要設定檔和引擎物件。 完成這個快速入門中的程式碼部分,加入程式碼來具現化設定檔和引擎物件:

  1. [方案總管] 中,開啟專案中包含 方法實作 main() 的 .cpp 檔案。 它預設為與包含它的專案相同的名稱,此專案名稱是在您建立專案時指定的。

  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. 用字串常數替換你貼上的原始碼中所有的佔位值:

    佔位符 價值 範例
    <應用程式識別碼> 「MIP SDK 設定與配置」文章的步驟 #2 中註冊之應用程式所指派的 Microsoft Entra 應用程式 ID(GUID)。 取代 2 個實例。 "00001111-aaaa-2222-bbbb-3333cccc4444"
    <應用程式名稱> 為您的應用程式設定的使用者友好名稱。 必須包含有效的 ASCII 字元(不包括 』;'),而且最好符合您在 Microsoft Entra 註冊中使用的應用程式名稱。 "AppInitialization"
    <應用程式版本> 應用程式的使用者定義版本資訊。 必須包含有效的 ASCII 字元(不包括 』;')。 "1.1.0.0"
    <引擎帳戶> 用於引擎身份識別的帳戶。 當您在令牌擷取期間使用使用者帳戶進行驗證時,必須符合此值。 "user1@tenant.onmicrosoft.com"
    <引擎狀態> 使用者定義的狀態將與引擎相關聯。 "My App State"
  2. 做最後的應用程式建置並解決任何錯誤。 你的程式碼應該能成功編譯,但還沒正常執行。 在完成下一個快速入門之前,你還沒有可提供的存取權杖。

下一步

現在您已完成初始化程式碼,可以繼續進行下一個快速入門,開始體驗 MIP File SDK。