Dynamics 365 Online通过OAuth 2 Client Credential授权(Server-to-Server Authentication)后调用Web API
微软动态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(, , ); //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 = ""});
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(, , ); //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 = "" });
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,是以该用户身份在运行的。

Dynamics 365 Online通过OAuth 2 Client Credential授权(Server-to-Server Authentication)后调用Web API的更多相关文章
- 利用Fiddler模拟通过Dynamics 365的OAuth 2 Client Credentials认证后调用Web API
微软动态CRM专家罗勇 ,回复337或者20190521可方便获取本文,同时可以在第一间得到我发布的最新博文信息,follow me. 配置Dynamics 365 & PowerApps 支 ...
- Dynamics 365 CE的插件/自定义工作流活动中调用Web API示例代码
微软动态CRM专家罗勇 ,回复325或者20190428可方便获取本文,同时可以在第一间得到我发布的最新博文信息,follow me! 现在Web API越来越流行,有时候为了程序更加健壮,需要在插件 ...
- Dynamics 365本地部署版本配置OAuth 2 Password Grant以调用Web API
微软动态CRM专家罗勇 ,回复330或者20190504可方便获取本文,同时可以在第一间得到我发布的最新博文信息,follow me! 根据官方建议,不要再使用Dynamics 365 Custome ...
- Dynamics CRM中跨域调用Web API 2
关注本人微信和易信公众号: 微软动态CRM专家罗勇 ,回复224或者20160611可方便获取本文,同时可以在第一间得到我发布的最新的博文信息,follow me!我的网站是 www.luoyong. ...
- 用Web API Client 调用 Web API
安装Web API客户端库 右键单击项目,选择管理 NuGet程序包,选择联机选项,选择全部,在搜索框中输入“Microsoft.AspNet.WebApi.Client”, 搜索结果就是要安装的类库 ...
- 解决Dynamics 365使用JS调用Web API时报no property value was found in the payload 错误。
摘要: 微软动态CRM专家罗勇 ,回复323或者20190421可方便获取本文,同时可以在第一间得到我发布的最新博文信息,follow me! 碰到如下报错: message: "An er ...
- 配置Postman通过OAuth 2 implicit grant获取Dynamics 365 CE Online实例的Access Token
微软动态CRM专家罗勇 ,回复335或者20190516可方便获取本文,同时可以在第一间得到我发布的最新博文信息,follow me. 对于测试Web API, Get 类型,不需要设定特别reque ...
- Dynamics CRM模拟OAuth请求获得Token后在外部调用Web API
关注本人微信和易信公众号: 微软动态CRM专家罗勇 ,回复233或者20161104可方便获取本文,同时可以在第一间得到我发布的最新的博文信息,follow me!我的网站是 www.luoyong. ...
- Dynamics 365 App for Outlook 与 Dynamics 365 for Outlook(已被弃用)
在最新的版本中Dynamics 365 for Outlook(Outlook 客户端)已被弃用 随 Dynamics CRM 2016(版本 8.0)引入的 Dynamics 365 App for ...
随机推荐
- Springboot vue.js html 跨域 前后分离 shiro权限 集成代码生成器
本代码为 Springboot vue.js 前后分离 + 跨域 版本 (权限控制到菜单和按钮) 后台框架:springboot2.1.2+ mybaits+maven+接口 前端页面:html + ...
- error: src refspec test does not match any.
我在本地创建了新分支test并提交到github上 错误信息如下: error: src refspec test does not match any. error: failed to push ...
- 一个故事搞懂Java并发编程
最近在给别人讲解Java并发编程面试考点时,为了解释锁对象这个概念,想了一个形象的故事.后来慢慢发现这个故事似乎能讲解Java并发编程中好多核心概念,于是完善起来形成了了这篇文章.大家先忘记并发编程, ...
- Kubernetes行业调研报告:多集群、多云部署成企业首选策略
新兴的多集群.多云部署成为首选的企业策略,而边缘部署则呈上升趋势 2019年11月5日,业界采用最广泛的Kubernetes管理平台创造者Rancher Labs(以下简称Rancher)发布了首份调 ...
- 如何在 Chrome中导出、导入书签和密码
目录 书签 密码 书签 1.导出 点击浏览器右上角的三小点,选择"书签",再选择"书签管理器",进入如下页面 点击蓝色书签栏右上角的三小点,选择"导出 ...
- ASP.NET Core Web 应用程序系列(五)- 在ASP.NET Core中使用AutoMapper进行实体映射
本章主要简单介绍下在ASP.NET Core中如何使用AutoMapper进行实体映射.在正式进入主题之前我们来看下几个概念: 1.数据库持久化对象PO(Persistent Object):顾名思义 ...
- 解决bcp导出CSV文件没有表头
思路: 1.输出表头文件到指定目录 2.bcp导出csv文件到temp目录 3.将以上导出文件与表头文件合并 4.删除temp目录下的文件 实现: create proc exportCSV ( @i ...
- jQuery实现下拉框默认选中
<form class="form-inline" method="post" action="/score_result/"> ...
- jquery 常用选择器基础语法学习
siblings方法的常用应用场景:选中高亮 实现代码 <!DOCTYPE html> <html> <head> <meta charset="U ...
- gradle下mybatis自动生成框架的使用
自动生成框架的意义 主要为了解决人为添加mapper,模型等工作,减少错误,提交效率! 添加引用build.gradle configurations { mybatisGenerator } myb ...