逐步解說:使用 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 筆客戶記錄。 [取得客戶] 按鈕為停用,如下面螢幕擷取畫面所示:
注意
初次從 Microsoft Dynamics 365 載入資料時速度可能會很慢,因為支援驗證的作業正在進行,但後續作業就會快很多。
最後,您可以按一下 [登出] 按鈕登出。
注意
此 SPA 應用程式不是要代表開發強大 SPA 應用程式的模式。 它經過簡化,著重在註冊和設定應用程式的程序。
建立 Web 應用程式專案
使用 Microsoft Visual Studio 2015 建立新的 [ASP.NET Web 應用程式] 專案,並使用 [空白] 範本。 您可以自由為專案命名。
您應該也能夠使用舊版 Microsoft Visual Studio,但這些步驟將描述使用 Visual Studio 2015。
新增名為 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>
將此頁面設定為專案的起始頁面
在專案的屬性中,選取 [Web],然後在 [伺服器] 下記下 [專案 URL]。 它應該像這樣 https://localhost:46575/。 記下產生的連接埠號碼。 在下一個步驟中需要此資訊。
在 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.
註冊應用程式
使用具有系統管理員權限的帳戶,登入Microsoft Azure 管理入口網站。 您必須使用與您要註冊應用程式所在相同 Office 365 訂閱 (用戶) 中的帳戶。 您也可以透過 Office 365 管理中心存取 Microsoft Azure 入口網站,方法是展開左側導覽窗格中的 [ADMIN] 項目並選取 [Azure AD]。
如果您沒有 Azure 組織用戶 (帳戶),或是您有帳戶但您在 Microsoft Dynamics 365 (線上) 的 Office 365 訂閱無法在您的 Azure 訂閱中使用,請依照主題設定開發人員網站的 Azure Active Directory 存取權限中的指示,讓兩個帳戶產生關聯。
如果您沒有帳戶,可以使用信用卡註冊一個帳戶。 不過,如果只是依照本主題所述的程序註冊一個或多個應用程式,應用程式註冊的帳戶是免費的,不會從您的信用卡收費。其他資訊:Active Directory 定價詳細資料
按一下頁面左欄的 [Active Directory]。 您可能需要捲動左欄才能看到 [Active Directory] 圖示和標籤。
在目錄清單中按一下所需的用戶目錄。
如果您的 Dynamics 365 組織用戶目錄未在目錄清單中出現,按一下 [新增],然後選取對話方塊中的 [使用現有目錄]。 遵循提供的提示與指示,然後回到步驟 1。
選取目標目錄後,按一下 [應用程式] (靠近頁面頂端),然後按一下 [新增]。
在 [您要如何處理?] 對話方塊中,按一下 [新增我的組織正在開發的應用程式]。
出現提示時,輸入您的應用程式名稱,例如 'SimpleSPA',選取類型:[Web 應用程式和/或 Web API],然後按一下向右箭頭繼續。 按一下問號 ?,取得有關每個輸入欄位的適當值的詳細資訊。
輸入下列資訊:
登入 URL
這是使用者登入後重新導向的目標 URL。 基於在 Visual Studio 中偵錯的目的,它應該是 https://localhost:####/SimpleSPA.html,其中 #### 代表您從 建立 Web 應用程式專案程序的步驟 4 取得的連接埠號碼。應用程式識別碼 URI
這必須是應用程式的唯一識別碼。 使用 https://XXXX.onmicrosoft.com/SimpleSPA,其中 XXXX 是 Active Directory 用戶。
選取新註冊應用程式的索引標籤後,按一下 [設定],尋找 [用戶端識別碼] 並複製它。
在 SimpleSPA.html 頁面中將 clientId 變數設定為此值。 參考建立 Web 應用程式專案程序的步驟 5。
向下捲動到頁面底部,並按一下 [新增應用程式]。 在對話方塊中,選取 [Dynamics 365 Online] 並關閉對話方塊。
在其他應用程式的權限底下,您會發現一列 [Dynamics 365 Online] 和 [委派的權限: 0]。 選取此列,並新增 [以組織使用者身分存取 Dynamics 365 (線上)]。
儲存應用程式註冊
在底部選取 [管理資訊清單] 並選擇 [下載資訊清單]。
開啟您下載的 JSON 檔案並尋找這行:"oauth2AllowImplicitFlow": false,,並將 false 變更為 true,然後儲存檔案。
再次返回 [管理資訊清單]。 選擇 [上傳資訊清單],並上傳您剛才儲存的 JSON 檔案。
偵錯應用程式
設定瀏覽器使用 Microsoft Edge、Google Chrome 或 Mozilla Firefox。
注意
在此情況下,Internet Explorer 無法進行偵錯。
按 F5 鍵開始偵錯。 您應預期此逐步解說的目標中所述的行為。
如果無法取得預期的結果,再次檢查您註冊應用程式和設定 SimpleSPA.html 程式碼時設定的值。
另請參閱
使用 OAuth 搭配跨原始來源資源共用,將單一頁面應用程式連線至 Microsoft Dynamics 365
Microsoft Dynamics 365
© 2017 Microsoft. 著作權所有,並保留一切權利。 著作權