微軟動態CRM專家羅勇 ,回復332或者20190505可方便獲取本文,同時可以在第一間得到我發布的最新博文信息,follow me!
本文很多內容來自 John Towgood 撰寫的 Dynamics 365 Online Authenticate with Client Credentials ,也着重參考了官方的 Use Single-Tenant server-to-server authentication ,我根據新的Azure Portal界面做了一些操作上的變化,並且改了一些代碼,還使用ADAL來簡化代碼。
登錄 https://portal.azure.com ,點擊左邊的 【Azure Active Directory】 ,然后再點擊 【App registrations】 ,再點擊【New registration】
輸入一個合適的名稱,Supported account types保持默認的 Accounts in this organizational directory only (Orgname) 不變,點擊【Register】按鈕。因為Redirect URI用不上所以不輸入。
注冊成功后會產生 Application (client) ID,記錄下來備用,同時也記錄下 Directory (tenant) ID。
再點擊左邊的【API Permissions】,再點擊右邊的 【+ Add a permission】按鈕。
選擇 【Dynamics CRM】 (也可以選擇使用 PowerApps Runtime Serive 這個權限),
選擇 【Delegated permissions】 > 【user_impersonation】后點擊【Add permissions】按鈕。
然后點擊【Grant admin consent for Orgname】,
在彈出的提示中選擇【Yes】。
然后點擊【Certificates & secrets】 > 【+ New client secret】,輸入合適的Description,在點擊【Add】按鈕。
會自動生成Client secrets,這里需要點擊生成的secret旁邊的【copy to clipboard】圖標將其復制下來,記得在這個步驟復制下來,因為離開這個頁面后就看不到這個secret了。
然后需要創建 一個Azure AD 用戶,點擊左側的【Azure Active Directory】> 【Users】。
然后點擊【New user】。
為用戶輸入Name,User Name,然后點擊【Create】按鈕。
最后還需要到Dynamics 365 Customer Engagement中創建一個Application User。導航到 Settings > Security > Users,切換到【Application Users】,點擊命令欄的【NEW】按鈕。
記得要切換到 APPLICATION USER這個窗體,輸入的內容如下,Application ID就是前面步驟記錄的Application (client) ID,其余的就是前面步驟創建的Azure AD user信息。
保存后會自動填充 Application ID URI 和 Azure AD Object ID 字段的值。
當然還需要給這個用戶授予至少一個角色才行,官方建議不要授予系統標准角色,我這里復制了一個標准角色授予給他。
如果用Postman來獲取access token的話,如下圖:
下面就是用代碼如何做了,不多說,看代碼:
using Newtonsoft.Json; using System; using System.Collections.Generic; using System.Net.Http; using System.Net.Http.Headers; using System.Threading.Tasks; namespace UsingWebAPI { public class AuthenticationResponse { public string access_token { get; set; } public int expires_in { get; set; } public int expires_on { get; set; } public int ext_expires_in { get; set; } public int not_before { get; set; } public string resource { get; set; } public string token_type { get; set; } } class Program { static string resourceUrl = "https://crm219270.crm5.dynamics.com/"; static string clientId = "de8dd947-a3e3-48ec-8602-c3063f11dc29"; static string clientSecret = "5FsXh2*oNyLRm]Go1a9hD.[]=k54GNOZ"; static string tenantId = "3e28b187-1c5c-42f5-a1be-3f47570da35d";
static void Main(string[] args) { GetAuthenticationResponse(); Console.ReadKey(); } private static async void GetAuthenticationResponse() { List<KeyValuePair<string, string>> vals = new List<KeyValuePair<string, string>>(); vals.Add(new KeyValuePair<string, string>("client_id", clientId)); vals.Add(new KeyValuePair<string, string>("resource", resourceUrl)); vals.Add(new KeyValuePair<string, string>("grant_type", "client_credentials")); vals.Add(new KeyValuePair<string, string>("client_secret", clientSecret)); string tokenUrl = string.Format("https://login.windows.net/{0}/oauth2/token", tenantId); using (HttpClient httpClient = new HttpClient()) { httpClient.DefaultRequestHeaders.Add("Cache-Control", "no-cache"); HttpContent content = new FormUrlEncodedContent(vals); HttpResponseMessage hrm = httpClient.PostAsync(tokenUrl, content).Result; AuthenticationResponse authenticationResponse = null; if (hrm.IsSuccessStatusCode) { string data = await hrm.Content.ReadAsStringAsync(); authenticationResponse = JsonConvert.DeserializeObject<AuthenticationResponse>(data); await DataOperations(authenticationResponse); } else { Console.WriteLine("Error." + hrm.ReasonPhrase); } } } private static async Task DataOperations(AuthenticationResponse authResult) { using (HttpClient httpClient = new HttpClient()) { httpClient.BaseAddress = new Uri(resourceUrl); httpClient.Timeout = new TimeSpan(0, 2, 0); //2 minutes httpClient.DefaultRequestHeaders.Add("OData-MaxVersion", "4.0"); httpClient.DefaultRequestHeaders.Add("OData-Version", "4.0"); httpClient.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json")); httpClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", authResult.access_token); string content = JsonConvert.SerializeObject(new { name = "A Account", telephone1 = "123456789"}); HttpRequestMessage request = new HttpRequestMessage(HttpMethod.Post, "api/data/v9.1/accounts"); request.Content = new StringContent(content); request.Content.Headers.ContentType = MediaTypeHeaderValue.Parse("application/json"); HttpResponseMessage response = await httpClient.SendAsync(request); if (response.IsSuccessStatusCode) { Console.WriteLine("Account created."); } else { Console.WriteLine(String.Format("Failed to create account, reason is '{0}'.", response.ReasonPhrase)); } } } } }
當然,如果使用ADAL的話,代碼會更加簡單點:
using Microsoft.IdentityModel.Clients.ActiveDirectory; using Newtonsoft.Json; using System; using System.Net.Http; using System.Net.Http.Headers; namespace UsingWebAPI { class Program { static string resourceUrl = "https://crm219270.crm5.dynamics.com/"; static string clientId = "de8dd947-a3e3-48ec-8602-c3063f11dc29"; static string clientSecret = "5FsXh2*oNyLRm]Go1a9hD.[]=k54GNOZ"; static string tenantId = "3e28b187-1c5c-42f5-a1be-3f47570da35d"; static void Main(string[] args) { AuthAndInvoke(); Console.ReadKey(); } private static async void AuthAndInvoke() { var credentials = new ClientCredential(clientId, clientSecret); var authContext = new AuthenticationContext("https://login.microsoftonline.com/" + tenantId); var result = await authContext.AcquireTokenAsync(resourceUrl, credentials); using (HttpClient httpClient = new HttpClient()) { httpClient.BaseAddress = new Uri(resourceUrl); httpClient.Timeout = new TimeSpan(0, 2, 0); //2 minutes httpClient.DefaultRequestHeaders.Add("OData-MaxVersion", "4.0"); httpClient.DefaultRequestHeaders.Add("OData-Version", "4.0"); httpClient.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json")); httpClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", result.AccessToken); string content = JsonConvert.SerializeObject(new { name = "A Account", telephone1 = "123456789" }); HttpRequestMessage request = new HttpRequestMessage(HttpMethod.Post, "api/data/v9.1/accounts"); request.Content = new StringContent(content); request.Content.Headers.ContentType = MediaTypeHeaderValue.Parse("application/json"); HttpResponseMessage response = await httpClient.SendAsync(request); if (response.IsSuccessStatusCode) { Console.WriteLine("Account created."); } else { Console.WriteLine(String.Format("Failed to create account, reason is '{0}'.", response.ReasonPhrase)); } } } } }
可以看到代碼創建的account的owner是我們前面步驟的Application User,是以該用戶身份在運行的。