연습: adal.js를 사용하여 SimpleSPA 응용 프로그램 등록 및 구성

 

게시 날짜: 2017년 1월

적용 대상: Dynamics 365 (online), Dynamics 365 (on-premises), Dynamics CRM 2016, Dynamics CRM Online

이 연습에서는 adal.js 및 원점 간 리소스 공유(CORS)를 사용하여 매우 단순한 단일 페이지 응용 프로그램(SPA)을 Microsoft Dynamics CRM Online 2016 업데이트의 데이터에 액세스하도록 등록 및 구성하는 과정을 설명합니다.추가 정보:원본간 리소스 공유에 OAuth를 사용하여 Microsoft Dynamics 365에 단일 페이지 응용 프로그램 연결.

필수 조건

  • Microsoft Dynamics CRM Online 2016 업데이트

  • Microsoft Dynamics 365(온라인)에 대해 관리자 역할을 가진 Microsoft Office 365 시스템 사용자 계정이 있어야 합니다.

  • 응용 프로그램 등록을 위한 Microsoft Azure 구독. 평가판 계정도 사용 가능합니다.

  • Microsoft Visual Studio 2015

이 연습의 목표

이 연습을 완료하면 Microsoft Dynamics 365(온라인)에서 사용자를 인증하고 데이터를 검색하는 기능을 제공하는 Visual Studio에서 단순한 SPA 응용 프로그램을 실행할 수 있습니다. 이 응용 프로그램은 하나의 HTML 페이지로 구성됩니다.

처음 응용 프로그램을 디버깅할 때는 로그인 단추만 표시됩니다.

로그인을 클릭하면 자격 증명을 입력할 수 있는 로그인 페이지에 연결됩니다.

자격 증명을 입력하면 로그인 단추가 숨겨지고 로그아웃 단추와 계정 가져오기 단추가 표시되는 HTML 페이지로 리디렉션됩니다. 또한 사용자 계정 정보를 사용한 인사말도 나타납니다.

계정 가져오기 단추를 클릭하여 Microsoft Dynamics 365 조직에서 10개의 계정 레코드를 검색할 수 있습니다. 다음 스크린샷과 같이 계정 가져오기 단추를 사용할 수 없습니다.

SimpleSPA 페이지

참고

인증 지원 작업 때문에 Microsoft Dynamics 365의 초기 데이터 로드 속도가 느릴 수 있지만 후속 작업은 훨씬 빠르게 진행됩니다.

마지막으로 로그아웃 단추를 클릭하여 로그아웃합니다.

참고

SPA 응용 프로그램은 복잡한 SPA 응용 프로그램 개발에 대한 패턴을 나타내기 위한 프로그램이 아닙니다. 응용 프로그램을 등록하고 구성하는 과정에 초점을 맞춰 간소화되어 있습니다.

웹 응용 프로그램 프로젝트 만들기

  1. Microsoft Visual Studio 2015를 사용하여 새 ASP.NET 웹 응용 프로그램 프로젝트를 만들고 빈 템플릿을 사용합니다. 원하는 프로젝트 이름을 지정할 수 있습니다.

    이전 버전의 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. 프로젝트의 속성에서 을 선택하고 서버 아래의 프로젝트 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 구독(테넌트)의 계정을 사용해야 합니다. 왼쪽 탐색 창에서 ADMIN 항목을 확장하고 Azure AD를 선택하여 Office 365 관리 센터를 통해 Microsoft Azure 포털에 액세스할 수도 있습니다.

    Azure 테넌트(계정)가 없거나 하나가 있기는 하지만 Office 365 구독(Microsoft Dynamics 365(온라인) 포함)을 Azure 구독에 사용할 수 없는 경우 항목 개발자 사이트를 위해 Azure Active Directory 액세스 설정의 지침에 따라 두 계정을 연결합니다.

    계정이 없을 경우 신용 카드를 사용하여 계정을 등록할 수 있습니다. 하지만 이 항목에 설명되는 있는 절차에 따라 하나 이상의 앱을 등록할 경우에만 응용 프로그램과 등록을 위한 계정은 무료이므로 신용 카드는 청구되지 않습니다.추가 정보:Active Directory 가격 내역

  2. 페이지의 왼쪽 열에서 Active Directory를 클릭합니다.Active Directory 아이콘 및 레이블을 표시하기 위해 왼쪽 열로 스크롤해야 할 수도 있습니다.

  3. 디렉터리 목록에서 원하는 테넌트 디렉터리를 클릭합니다.

    사용 가능한 Active Directory 항목 목록

    Dynamics 365 테넌트 디렉터리가 디렉터리 목록에 표시되지 않으면 추가를 선택한 후 대화 상자에서 기존 디렉터리 사용을 클릭합니다. 제공된 프롬프트 및 지시를 따른 후 1단계로 다시 이동합니다.

  4. 대상 디렉터리를 선택하고 응용 프로그램(페이지 상단 근처)을 클릭한 후 추가를 클릭합니다.

  5. 무엇을 하시겠습니까? 대화 상자에서 내 조직에서 개발 중인 응용 프로그램 추가를 클릭합니다.

  6. 메시지가 표시되면 응용 프로그램 이름(예: SimpleSPA)을 입력하고 웹 응용 프로그램 및/또는 웹 API를 클릭한 후 오른쪽 화살표를 클릭하여 계속합니다. 각 입력 필드에 적합한 값에 대한 자세한 내용을 보려면 물음표 **?**를 클릭합니다.

  7. 다음 정보를 입력합니다.

    • 로그인 URL
      사용자가 로그인하면 리디렉션될 URL입니다.Visual Studio에서 디버깅하기 위해 ####이 웹 응용 프로그램 프로젝트 만들기 절차의 4단계에서 얻은 포트 번호를 나타내는 https://localhost:####/SimpleSPA.html이어야 합니다.

    • 앱 ID URI
      응용 프로그램에 대한 고유 식별자이어야 합니다. XXXX가 Active Directory 테넌트인 https://XXXX.onmicrosoft.com/SimpleSPA를 사용합니다.

  8. 선택된 새로 등록된 응용 프로그램의 탭에서 구성을 클릭하고 클라이언트 ID를 찾아 복사합니다.

    SimpleSPA.html 페이지의 clientId 변수를 이 값으로 설정합니다.웹 응용 프로그램 프로젝트 만들기 절차의 5단계를 참조하십시오.

  9. 페이지의 아래쪽으로 스크롤하여 응용 프로그램 추가를 클릭합니다. 대화 상자에서 Dynamics 365 Online을 선택하고 대화 상자를 닫습니다.

  10. 다른 응용 프로그램 사용 권한 아래에서 Dynamics 365 Online위임된 권한: 0에 대한 행을 찾을 수 있습니다. 이를 선택하고 조직 사용자로 Dynamics 365(온라인)에 액세스를 추가합니다.

  11. 응용 프로그램 등록 저장

  12. 아래에서 매니페스트 관리를 선택하고 매니페스트 다운로드를 선택합니다.

  13. 다운로드한 JSON 파일을 열고 "oauth2AllowImplicitFlow": false, 줄을 찾아 falsetrue로 변경한 다음 파일을 저장합니다.

  14. 다시 매니페스트 관리로 돌아갑니다.매니페스트 업로드를 선택하고 방금 저장한 JSON 파일을 업로드합니다.

응용 프로그램 디버깅

  1. Microsoft Edge, Google Chrome, 또는 Mozilla Firefox를 사용하도록 브라우저를 설정합니다.

    참고

    Internet Explorer는 이 상황에서 디버깅을 작동하지 않습니다.

  2. F5 키를 눌러 디버깅을 시작합니다.이 연습의 목표에 설명된 동작이 수행됩니다.

    원하는 결과를 얻지 못하면 응용 프로그램을 등록하고 SimpleSPA.html 코드를 구성할 때 설정한 값을 다시 확인하십시오.

참고 항목

원본간 리소스 공유에 OAuth를 사용하여 Microsoft Dynamics 365에 단일 페이지 응용 프로그램 연결

Microsoft Dynamics 365

© 2017 Microsoft. All rights reserved. 저작권 정보