共用方式為


逐步解說:使用 adal.js 註冊及設定 SimpleSPA 應用程式

 

發行︰ 2017年1月

適用於: Dynamics 365 (online)、Dynamics 365 (on-premises)、Dynamics CRM 2016、Dynamics CRM Online

此逐步解說將描述註冊和設定最簡單的單一頁面應用程式 (SPA) 的程序,在 Microsoft Dynamics CRM Online 2016 更新 中存取資料,使用 adal.js 和跨原始來源資源共用 (CORS)。其他資訊:使用 OAuth 搭配跨原始來源資源共用,將單一頁面應用程式連線至 Microsoft Dynamics 365

必要條件

  • Microsoft Dynamics CRM Online 2016 更新

  • 您必須具有 Microsoft Office 365 系統管理員角色的 Microsoft Dynamics 365 (線上) 系統使用者帳戶。

  • 應用程式註冊的 Microsoft Azure 訂閱。 試用帳戶也可運作。

  • Microsoft Visual Studio 2015

此逐步解說的目標

當您完成此逐步解說,將能夠在 Visual Studio 中執行簡單的 SPA 應用程式,提供使用者驗證和從 Microsoft Dynamics 365 (線上) 擷取資料的能力。 此應用程式由單一 HTML 頁面組成。

當您初次對應用程式偵錯時,只會有一個 [登入] 按鈕。

按一下 [登入],就會將您重新導向登入頁面輸入您的認證。

輸入認證後,您將再次回到 HTML 頁面,當中 [登入] 按鈕會隱藏,並顯示 [登出] 按鈕和 [取得客戶] 按鈕。 您也會看見使用您的使用者帳戶資訊的問候語。

按一下 [取得客戶] 按鈕會從您的 Microsoft Dynamics 365 組織擷取 10 筆客戶記錄。 [取得客戶] 按鈕為停用,如下面螢幕擷取畫面所示:

SimpleSPA 頁面

注意

初次從 Microsoft Dynamics 365 載入資料時速度可能會很慢,因為支援驗證的作業正在進行,但後續作業就會快很多。

最後,您可以按一下 [登出] 按鈕登出。

注意

此 SPA 應用程式不是要代表開發強大 SPA 應用程式的模式。 它經過簡化,著重在註冊和設定應用程式的程序。

建立 Web 應用程式專案

  1. 使用 Microsoft Visual Studio 2015 建立新的 [ASP.NET Web 應用程式] 專案,並使用 [空白] 範本。 您可以自由為專案命名。

    您應該也能夠使用舊版 Microsoft Visual Studio,但這些步驟將描述使用 Visual Studio 2015。

  2. 新增名為 SimpleSPA.html 的新 HTML 頁面至專案,並貼入下列程式碼:

    <!DOCTYPE html>
    <html>
    <head>
     <title>Simple SPA</title>
     <meta charset="utf-8" />
     <script src="https://secure.aadcdn.microsoftonline-p.com/lib/1.0.0/js/adal.min.js"></script>
     <script type="text/javascript">
      "use strict";
    
      //Set these variables to match your environment
      var organizationURI = "https:// [organization name].crm.dynamics.com"; //The URL to connect to CRM (online)
      var tenant = "[xxx.onmicrosoft.com]"; //The name of the Azure AD organization you use
      var clientId = "[client id]"; //The ClientId you got when you registered the application
      var pageUrl = "https://localhost: [PORT #]/SimpleSPA.html"; //The URL of this page in your development environment when debugging.
    
    
      var user, authContext, message, errorMessage, loginButton, logoutButton, getAccountsButton, accountsTable, accountsTableBody;
    
      //Configuration data for AuthenticationContext
      var endpoints = {
       orgUri: organizationURI
      };
    
      window.config = {
       tenant: tenant,
       clientId: clientId,
       postLogoutRedirectUri: pageUrl,
       endpoints: endpoints,
       cacheLocation: 'localStorage', // enable this for IE, as sessionStorage does not work for localhost.
      };
    
    
      document.onreadystatechange = function () {
       if (document.readyState == "complete") {
    
        //Set DOM elements referenced by scripts
        message = document.getElementById("message");
        errorMessage = document.getElementById("errorMessage");
        loginButton = document.getElementById("login");
        logoutButton = document.getElementById("logout");
        getAccountsButton = document.getElementById("getAccounts");
        accountsTable = document.getElementById("accountsTable");
        accountsTableBody = document.getElementById("accountsTableBody");
    
        //Event handlers on DOM elements
        loginButton.addEventListener("click", login);
        logoutButton.addEventListener("click", logout);
        getAccountsButton.addEventListener("click", getAccounts);
    
        //call authentication function
        authenticate();
    
        if (user) {
         loginButton.style.display = "none";
         logoutButton.style.display = "block";
         getAccountsButton.style.display = "block";
    
         var helloMessage = document.createElement("p");
         helloMessage.textContent = "Hello " + user.profile.name;
         message.appendChild(helloMessage)
    
        }
        else {
         loginButton.style.display = "block";
         logoutButton.style.display = "none";
         getAccountsButton.style.display = "none";
        }
    
       }
      }
    
      // Function that manages authentication
      function authenticate() {
       //OAuth context
       authContext = new AuthenticationContext(config);
    
       // Check For & Handle Redirect From AAD After Login
       var isCallback = authContext.isCallback(window.location.hash);
       if (isCallback) {
        authContext.handleWindowCallback();
       }
       var loginError = authContext.getLoginError();
    
       if (isCallback && !loginError) {
        window.location = authContext._getItem(authContext.CONSTANTS.STORAGE.LOGIN_REQUEST);
       }
       else {
        errorMessage.textContent = loginError;
       }
       user = authContext.getCachedUser();
    
      }
    
      //function that logs in the user
      function login() {
       authContext.login();
      }
      //function that logs out the user
      function logout() {
       authContext.logOut();
       accountsTable.style.display = "none";
       accountsTableBody.innerHTML = "";
      }
    
    
    //function that initiates retrieval of accounts
      function getAccounts() {
    
       getAccountsButton.disabled = true;
       var retrievingAccountsMessage = document.createElement("p");
       retrievingAccountsMessage.textContent = "Retrieving 10 accounts from " + organizationURI + "/api/data/v8.0/accounts";
       message.appendChild(retrievingAccountsMessage)
    
       // Function to perform operation is passed as a parameter to the aquireToken method
       authContext.acquireToken(organizationURI, retrieveAccounts)
    
      }
    
    
    //Function that actually retrieves the accounts
      function retrieveAccounts(error, token) {
       // Handle ADAL Errors.
       if (error || !token) {
        errorMessage.textContent = 'ADAL error occurred: ' + error;
        return;
       }
    
       var req = new XMLHttpRequest()
       req.open("GET", encodeURI(organizationURI + "/api/data/v8.0/accounts?$select=name,address1_city&$top=10"), true);
       //Set Bearer token
       req.setRequestHeader("Authorization", "Bearer " + token);
       req.setRequestHeader("Accept", "application/json");
       req.setRequestHeader("Content-Type", "application/json; charset=utf-8");
       req.setRequestHeader("OData-MaxVersion", "4.0");
       req.setRequestHeader("OData-Version", "4.0");
       req.onreadystatechange = function () {
        if (this.readyState == 4 /* complete */) {
         req.onreadystatechange = null;
         if (this.status == 200) {
          var accounts = JSON.parse(this.response).value;
          renderAccounts(accounts);
         }
         else {
          var error = JSON.parse(this.response).error;
          console.log(error.message);
          errorMessage.textContent = error.message;
         }
        }
       };
       req.send();
      }
      //Function that writes account data to the accountsTable
      function renderAccounts(accounts) {
       accounts.forEach(function (account) {
        var name = account.name;
        var city = account.address1_city;
        var nameCell = document.createElement("td");
        nameCell.textContent = name;
        var cityCell = document.createElement("td");
        cityCell.textContent = city;
        var row = document.createElement("tr");
        row.appendChild(nameCell);
        row.appendChild(cityCell);
        accountsTableBody.appendChild(row);
       });
       accountsTable.style.display = "block";
      }
    
     </script>
     <style>
      body {
       font-family: 'Segoe UI';
      }
    
      table {
       border-collapse: collapse;
      }
    
      td, th {
       border: 1px solid black;
      }
    
      #errorMessage {
       color: red;
      }
    
      #message {
       color: green;
      }
     </style>
    </head>
    <body>
     <button id="login">Login</button>
     <button id="logout" style="display:none;">Logout</button>
     <button id="getAccounts" style="display:none;">Get Accounts</button>
     <div id="errorMessage"></div>
     <div id="message"></div>
     <table id="accountsTable" style="display:none;">
      <thead><tr><th>Name</th><th>City</th></tr></thead>
      <tbody id="accountsTableBody"></tbody>
     </table>
    
    </body>
    </html>
    
  3. 將此頁面設定為專案的起始頁面

  4. 在專案的屬性中,選取 [Web],然後在 [伺服器] 下記下 [專案 URL]。 它應該像這樣 https://localhost:46575/。 記下產生的連接埠號碼。 在下一個步驟中需要此資訊。

  5. 在 SimpleSPA.html 頁面內尋找下列設定變數,並對應地進行設定。 完成逐步解說的下一個部分後,您將能夠設定 clientId

    //Set these variables to match your environment
    var organizationURI = "https://[organization name].crm.dynamics.com"; //The URL to connect to CRM (online)
    var tenant = "[xxx.onmicrosoft.com]"; //The name of the Azure AD organization you use
    var clientId = "[client id]"; //The ClientId you got when you registered the application
    var pageUrl = "https://localhost:[PORT #]/SimpleSPA.html"; //The URL of this page in your development environment when debugging.
    

註冊應用程式

  1. 使用具有系統管理員權限的帳戶,登入Microsoft Azure 管理入口網站。 您必須使用與您要註冊應用程式所在相同 Office 365 訂閱 (用戶) 中的帳戶。 您也可以透過 Office 365 管理中心存取 Microsoft Azure 入口網站,方法是展開左側導覽窗格中的 [ADMIN] 項目並選取 [Azure AD]。

    如果您沒有 Azure 組織用戶 (帳戶),或是您有帳戶但您在 Microsoft Dynamics 365 (線上) 的 Office 365 訂閱無法在您的 Azure 訂閱中使用,請依照主題設定開發人員網站的 Azure Active Directory 存取權限中的指示,讓兩個帳戶產生關聯。

    如果您沒有帳戶,可以使用信用卡註冊一個帳戶。 不過,如果只是依照本主題所述的程序註冊一個或多個應用程式,應用程式註冊的帳戶是免費的,不會從您的信用卡收費。其他資訊:Active Directory 定價詳細資料

  2. 按一下頁面左欄的 [Active Directory]。 您可能需要捲動左欄才能看到 [Active Directory] 圖示和標籤。

  3. 在目錄清單中按一下所需的用戶目錄。

    可用的 Active Directory 項目清單

    如果您的 Dynamics 365 組織用戶目錄未在目錄清單中出現,按一下 [新增],然後選取對話方塊中的 [使用現有目錄]。 遵循提供的提示與指示,然後回到步驟 1。

  4. 選取目標目錄後,按一下 [應用程式] (靠近頁面頂端),然後按一下 [新增]。

  5. 在 [您要如何處理?] 對話方塊中,按一下 [新增我的組織正在開發的應用程式]。

  6. 出現提示時,輸入您的應用程式名稱,例如 'SimpleSPA',選取類型:[Web 應用程式和/或 Web API],然後按一下向右箭頭繼續。 按一下問號 ?,取得有關每個輸入欄位的適當值的詳細資訊。

  7. 輸入下列資訊:

    • 登入 URL
      這是使用者登入後重新導向的目標 URL。 基於在 Visual Studio 中偵錯的目的,它應該是 https://localhost:####/SimpleSPA.html,其中 #### 代表您從 建立 Web 應用程式專案程序的步驟 4 取得的連接埠號碼。

    • 應用程式識別碼 URI
      這必須是應用程式的唯一識別碼。 使用 https://XXXX.onmicrosoft.com/SimpleSPA,其中 XXXX 是 Active Directory 用戶。

  8. 選取新註冊應用程式的索引標籤後,按一下 [設定],尋找 [用戶端識別碼] 並複製它。

    在 SimpleSPA.html 頁面中將 clientId 變數設定為此值。 參考建立 Web 應用程式專案程序的步驟 5。

  9. 向下捲動到頁面底部,並按一下 [新增應用程式]。 在對話方塊中,選取 [Dynamics 365 Online] 並關閉對話方塊。

  10. 在其他應用程式的權限底下,您會發現一列 [Dynamics 365 Online] 和 [委派的權限: 0]。 選取此列,並新增 [以組織使用者身分存取 Dynamics 365 (線上)]。

  11. 儲存應用程式註冊

  12. 在底部選取 [管理資訊清單] 並選擇 [下載資訊清單]。

  13. 開啟您下載的 JSON 檔案並尋找這行:"oauth2AllowImplicitFlow": false,,並將 false 變更為 true,然後儲存檔案。

  14. 再次返回 [管理資訊清單]。 選擇 [上傳資訊清單],並上傳您剛才儲存的 JSON 檔案。

偵錯應用程式

  1. 設定瀏覽器使用 Microsoft Edge、Google Chrome 或 Mozilla Firefox。

    注意

    在此情況下,Internet Explorer 無法進行偵錯。

  2. 按 F5 鍵開始偵錯。 您應預期此逐步解說的目標中所述的行為。

    如果無法取得預期的結果,再次檢查您註冊應用程式和設定 SimpleSPA.html 程式碼時設定的值。

另請參閱

使用 OAuth 搭配跨原始來源資源共用,將單一頁面應用程式連線至 Microsoft Dynamics 365

Microsoft Dynamics 365

© 2017 Microsoft. 著作權所有,並保留一切權利。 著作權