Показать сообщение отдельно
Старый 07.07.2020, 17:54   #2  
sukhanchik is offline
sukhanchik
Administrator
Аватар для sukhanchik
MCBMSS
Злыдни
Лучший по профессии 2015
Лучший по профессии AXAWARD 2013
Лучший по профессии 2011
Лучший по профессии 2009
 
3,283 / 3491 (123) ++++++++++
Регистрация: 13.06.2004
Адрес: Москва
D365FO: Аутенфикация при использовании веб-сервиса
Независимо от применяемой технологии SOAP / JSON отдельной задачей стоит аутенфикация внешнего приложения при подключении к D365FO.
Для локальной (On premise) версии аутенфикация осуществляется через логин / пароль, которые проверяются службой ADFS (Active Directory Federation Services)
Для облачной (Cloud) версии аутенфикация осуществляется через токен, т.е. большую текстовую строку, которая возвращается в приложение службой аутенфикации. Токен вычисляется на основе 4-х параметров:
  • Tenant: Тенант (а точнее конкретно в данном случае - домен учетной записи пользователя, т.е. если пользователь D365FO, под которым будет обращение к веб-сервису - ассоциирован с учетной записью web-service@ivan-petrov.ru, то в данном случае потребуется значение "ivan-petrov.ru")
  • Resource: Ресурс, т.е. URL системы; для OneBox - это "https://usnconeboxax1aos.cloud.onebox.dynamics.com"
  • AppId: идентификатор приложения в Azure Active Directory (AAD)
  • AppSecret: секретный идентификатор, который генерируется в приложении в AAD

Образец кода для аутенфикации лежит в https://github.com/microsoft/Dynamic...OAuthHelper.cs в методе GetAuthenticationHeader.
X++:
using Microsoft.IdentityModel.Clients.ActiveDirectory;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace AuthenticationUtility
{
    public class OAuthHelper
    {
        /// <summary>
        /// The header to use for OAuth authentication.
        /// </summary>
        public const string OAuthHeader = "Authorization";

        /// <summary>
        /// Retrieves an authentication header from the service.
        /// </summary>
        /// <returns>The authentication header for the Web API call.</returns>
        public static string GetAuthenticationHeader(bool useWebAppAuthentication = false)
        {
            string aadTenant = ClientConfiguration.Default.ActiveDirectoryTenant;
            string aadClientAppId = ClientConfiguration.Default.ActiveDirectoryClientAppId;
            string aadClientAppSecret = ClientConfiguration.Default.ActiveDirectoryClientAppSecret;
            string aadResource = ClientConfiguration.Default.ActiveDirectoryResource;

            AuthenticationContext authenticationContext = new AuthenticationContext(aadTenant, false);
            AuthenticationResult authenticationResult;

            if (useWebAppAuthentication)
            {
                if (string.IsNullOrEmpty(aadClientAppSecret))
                {
                    Console.WriteLine("Please fill AAD application secret in ClientConfiguration if you choose authentication by the application.");
                    throw new Exception("Failed OAuth by empty application secret.");
                }

                try
                {
                    // OAuth through application by application id and application secret.
                    var creadential = new ClientCredential(aadClientAppId, aadClientAppSecret);
                    authenticationResult = authenticationContext.AcquireTokenAsync(aadResource, creadential).Result;
                }
                catch (Exception ex)
                {
                    Console.WriteLine(string.Format("Failed to authenticate with AAD by application with exception {0} and the stack trace {1}", ex.ToString(), ex.StackTrace));
                    throw new Exception("Failed to authenticate with AAD by application.");
                }
            }
            else
            {
                // OAuth through username and password.
                string username = ClientConfiguration.Default.UserName;
                string password = ClientConfiguration.Default.Password;

                if (string.IsNullOrEmpty(password))
                {
                    Console.WriteLine("Please fill user password in ClientConfiguration if you choose authentication by the credential.");
                    throw new Exception("Failed OAuth by empty password.");
                }

                try
                {
                    // Get token object
                    var userCredential = new UserPasswordCredential(username, password); ;
                    authenticationResult = authenticationContext.AcquireTokenAsync(aadResource, aadClientAppId, userCredential).Result;
                }
                catch (Exception ex)
                {
                    Console.WriteLine(string.Format("Failed to authenticate with AAD by the credential with exception {0} and the stack trace {1}", ex.ToString(), ex.StackTrace));
                    throw new Exception("Failed to authenticate with AAD by the credential.");
                }
            }

            // Create and get JWT token
            return authenticationResult.CreateAuthorizationHeader();
        }
    }
}
Его вызов с параметром true авторизует внешнее приложение по токену. Параметр false - по логину и паролю. Параметры подключения прописываются в файле https://github.com/microsoft/Dynamic...nfiguration.cs
X++:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace AuthenticationUtility
{
    public partial class ClientConfiguration
    {
        public static ClientConfiguration Default { get { return ClientConfiguration.OneBox; } }

        public static ClientConfiguration OneBox = new ClientConfiguration()
        {
            // You only need to populate this section if you are logging on via a native app. For Service to Service scenarios in which you e.g. use a service principal you don't need that.
            UriString = "https://usnconeboxax1aos.cloud.onebox.dynamics.com/",
            UserName = "tusr1@TAEOfficial.ccsctp.net",            
            // Insert the correct password here for the actual test.
            Password = "",

            // You need this only if you logon via service principal using a client secret. See: [url]https://docs.microsoft.com/en-us/dynamics365/unified-operations/dev-itpro/data-entities/services-home-page[/url] to get more data on how to populate those fields.
            // You can find that under AAD in the azure portal
            ActiveDirectoryResource = "https://usnconeboxax1aos.cloud.onebox.dynamics.com", // Don't have a trailing "/". Note: Some of the sample code handles that issue.
            ActiveDirectoryTenant = "https://login.windows-ppe.net/TAEOfficial.ccsctp.net", // Some samples: [url]https://login.windows.net/yourtenant.onmicrosoft.com[/url], [url]https://login.windows.net/microsoft.com[/url]
            ActiveDirectoryClientAppId = "d8a9a121-b463-41f6-a86c-041272bdb340",
            // Insert here the application secret when authenticate with AAD by the application
            ActiveDirectoryClientAppSecret = "",

            // Change TLS version of HTTP request from the client here
            // Ex: TLSVersion = "1.2"
            // Leave it empty if want to use the default version
            TLSVersion = "",
        };

        public string TLSVersion { get; set; }
        public string UriString { get; set; }
        public string UserName { get; set; }
        public string Password { get; set; }
        public string ActiveDirectoryResource { get; set; }
        public String ActiveDirectoryTenant { get; set; }
        public String ActiveDirectoryClientAppId { get; set; }
        public string ActiveDirectoryClientAppSecret { get; set; }
    }
}
Поскольку OneBox - виртуалка предполагает подключение к AAD, а не к ADFS - я опишу ниже схему настройки AAD для подключения через токен (авторизацию по логину и паролю с ходу у меня не получилось сделать).

Я изначально предполагаю, что имеется виртуалка OneBox, пользователь Admin которой сопоставлен с какой-нибудь учетной записью типа vasya@ivan-petrov.ru и есть возможность (в т.ч. права) под какой-нибудь учетной записью из домена @ivan-petrov.ru сделать настройки в Azure Active Directory.
Итак, заходим на https://portal.azure.com/ и выбираем ярлык Azure Active Directory
Название: SNAG_Program-0039.png
Просмотров: 870

Размер: 9.2 Кб

Слева выбираем пункт Регистрация приложений, жмем кнопку Новая регистрация и указываем имя приложения. Впоследствии мы свяжем данное приложение с пользователем, под которым внешнее приложение будет подключаться к нашему экземпляру системы D365FO, поэтому я указываю в качестве названия приложения - название нашего веб-сервиса
Нажмите на изображение для увеличения
Название: SNAG_Program-0040.png
Просмотров: 106
Размер:	30.1 Кб
ID:	12874
Нажмите на изображение для увеличения
Название: SNAG_Program-0041.png
Просмотров: 96
Размер:	17.2 Кб
ID:	12875
Нажмите на изображение для увеличения
Название: SNAG_Program-0042.png
Просмотров: 92
Размер:	10.3 Кб
ID:	12876
Больше ничего указывать не обязательно, поэтому можно нажать на кнопку Зарегистрировать.
После этого у нас откроется страничка, где уже будет нашему приложению присвоен Идентификатор приложения, он же Application Id (AppId), который мы впоследствии укажем в "четверке" параметров подключения в поле AppId
Нажмите на изображение для увеличения
Название: SNAG_Program-0043.png
Просмотров: 96
Размер:	17.6 Кб
ID:	12877

Далее этому приложению нужно добавить секретный идентификатор, тот - который у нас будет указан в поле AppSecretId. Для этого, не уходя со странички приложения выбираем слева пункт "Сертификаты и секреты"
Название: SNAG_Program-0044.png
Просмотров: 868

Размер: 23.6 Кб
И нажимаем кнопку "Новый секрет клиента"
Нажмите на изображение для увеличения
Название: SNAG_Program-0045.png
Просмотров: 95
Размер:	76.3 Кб
ID:	12879
Указываем описание (любое) и срок действия - 1, 2 года или бессрочно и жмем кнопку Добавить
Название: SNAG_Program-0046.png
Просмотров: 870

Размер: 13.2 Кб
Мы получаем секретный идентификатор, который впоследствии и нужно будет подставить в поле AppSecret. Его желательно сразу сохранить, т.к. потом на этой странице он уже отображаться не будет и его придется заново генерировать (в то время, как идентификатор приложения можно будет увидеть при повторном открытии страницы).
Нажмите на изображение для увеличения
Название: SNAG_Program-0047.png
Просмотров: 85
Размер:	10.1 Кб
ID:	12881
Теперь нам нужно ассоциировать наше приложение с пользователем в D365FO. Для этого нужно зайти в систему в модуль Администрирование и выбрать пункт меню Настройка - Приложения Azure Active Directory
Нажмите на изображение для увеличения
Название: SNAG_Program-0048.png
Просмотров: 106
Размер:	44.6 Кб
ID:	12882
И настроить соответствие между идентификатором приложения (AppId, код клиента) и пользователем системы (пользователь может и должен быть ограничен в правах)
Нажмите на изображение для увеличения
Название: SNAG_Program-0049.png
Просмотров: 100
Размер:	51.5 Кб
ID:	12883

Дело осталось за малым - подготовить проект на C#, используя заготовки Microsoft и полученные идентификаторы AppId и AppSecret.
На всякий случай отмечу, что в данном примере у меня получились следующие значения:
Цитата:
AppId: "d4a93f6d-1d46-4e33-a258-3aa3ae7cb819"
AppSecretId: "J6Z86h8.JhXxYH.43TM5Bap7~_UZ29wI_R"
Tenant: "ivan-petrov.ru"
Resource: "https://usnconeboxax1aos.cloud.onebox.dynamics.com"
Итоговый файл ClientConfiguration у нас получится такой:
X++:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace AuthenticationUtility
{
    public partial class ClientConfiguration
    {
        public static ClientConfiguration Default { get { return ClientConfiguration.OneBox; } }

        public static ClientConfiguration OneBox = new ClientConfiguration()
        {
            // You only need to populate this section if you are logging on via a native app. For Service to Service scenarios in which you e.g. use a service principal you don't need that.
            UriString = "",
            UserName = "",
            // Insert the correct password here for the actual test.
            Password = "",

            // You need this only if you logon via service principal using a client secret. See: [url]https://docs.microsoft.com/en-us/dynamics365/unified-operations/dev-itpro/data-entities/services-home-page[/url] to get more data on how to populate those fields.
            // You can find that under AAD in the azure portal
            ActiveDirectoryResource = "https://usnconeboxax1aos.cloud.onebox.dynamics.com", // Don't have a trailing "/". Note: Some of the sample code handles that issue.
            ActiveDirectoryTenant = "https://login.windows.net/ivan-petrov.ru", // Some samples: [url]https://login.windows.net/yourtenant.onmicrosoft.com[/url], [url]https://login.windows.net/microsoft.com[/url]
            ActiveDirectoryClientAppId = "d4a93f6d-1d46-4e33-a258-3aa3ae7cb819",
            // Insert here the application secret when authenticate with AAD by the application
            ActiveDirectoryClientAppSecret = "J6Z86h8.JhXxYH.43TM5Bap7~_UZ29wI_R",

            // Change TLS version of HTTP request from the client here
            // Ex: TLSVersion = "1.2"
            // Leave it empty if want to use the default version
            TLSVersion = "",
        };

        public string TLSVersion { get; set; }
        public string UriString { get; set; }
        public string UserName { get; set; }
        public string Password { get; set; }
        public string ActiveDirectoryResource { get; set; }
        public String ActiveDirectoryTenant { get; set; }
        public String ActiveDirectoryClientAppId { get; set; }
        public string ActiveDirectoryClientAppSecret { get; set; }
    }
}
Он отличается от оригинального:
- Отсутствием значений переменных UserName, Password и UriString
- Заполненными значениями переменных ActiveDirectoryResource (URL нашей системы D365FO), ActiveDirectoryTenant (строка "https://login.windows.net/", к которой добавлен наш домен ivan-petrov.ru), ActiveDirectoryClientAppId и ActiveDirectoryClientAppSecret, полученных при регистрации приложения в Azure AD
__________________
Возможно сделать все. Вопрос времени

Последний раз редактировалось sukhanchik; 08.07.2020 в 00:03.