使用ADAL C#作为机密用户/守护程序服务器/服务器到服务器 – 401
参考未回答的问题:
401- Unauthorized authentication using REST API Dynamics CRM with Azure AD 和 Dynamics CRM Online 2016 – Daemon / Server application Azure AD authentication error to Web Api 和 Dynamics CRM 2016 Online Rest API with client credentials OAuth flow 我需要在天蓝云中的Web服务和Dynamics CRM Online 2016之间进行通信,而无需任何登录屏幕!该服务将有一个REST api,可以触发CRM上的CRUD操作(我也将实现身份验证) 我认为这称为“机密客户端”或“守护程序服务器”或只是“服务器到服务器” 我在Azure AD中正确设置了我的服务(“委托权限=在线访问动态作为组织用户”,没有其他选项) 我在VS中创建了一个ASP.NET WEB API项目,它在Azure中创建了我的WebService,并在CRM的Azure AD中创建了“应用程序”. 我的代码看起来像这样(请忽略EntityType和returnValue): public class WolfController : ApiController { private static readonly string Tenant = "xxxxx.onmicrosoft.com"; private static readonly string ClientId = "dxxx53-42xx-43bc-b14e-c1e84b62752d"; private static readonly string Password = "j+t/DXjn4PMVAHSvZGd5sptGxxxxxxxxxr5Ki8KU="; // client secret,valid for one or two years private static readonly string ResourceId = "https://tenantname-naospreview.crm.dynamics.com/"; public static async Task<AuthenticationResult> AcquireAuthentificationToken() { AuthenticationContext authenticationContext = new AuthenticationContext("https://login.windows.net/"+ Tenant); ClientCredential clientCredentials = new ClientCredential(ClientId,Password); return await authenticationContext.AcquireTokenAsync(ResourceId,clientCredentials); } // GET: just for calling the DataOperations-method via a GET,ignore the return public async Task<IEnumerable<Wolf>> Get() { AuthenticationResult result = await AcquireAuthentificationToken(); await DataOperations(result); return new Wolf[] { new Wolf() }; } private static async Task DataOperations(AuthenticationResult authResult) { using (HttpClient httpClient = new HttpClient()) { httpClient.BaseAddress = new Uri(ResourceId); 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.AccessToken); Account account = new Account(); account.name = "Test Account"; account.telephone1 = "555-555"; string content = String.Empty; content = JsonConvert.SerializeObject(account,new JsonSerializerSettings() {DefaultValueHandling = DefaultValueHandling.Ignore}); //Create Entity///////////////////////////////////////////////////////////////////////////////////// HttpRequestMessage request = new HttpRequestMessage(HttpMethod.Post,"api/data/v8.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 '{0}' created.",account.name); } else //Getting Unauthorized here { throw new Exception(String.Format("Failed to create account '{0}',reason is '{1}'.",account.name,response.ReasonPhrase)); } ... and more code 在调用我的GET请求时,我获得了401 Unauthorized,尽管我已经获得并发送了AccessToken. 有任何想法吗? 编辑: https://samlman.wordpress.com/2015/06/04/getting-an-azure-access-token-for-a-web-application-entirely-in-code/ 使用此代码: public class WolfController : ApiController { private static readonly string Tenant = System.Configuration.ConfigurationManager.AppSettings["ida:Tenant"]; private static readonly string TenantGuid = System.Configuration.ConfigurationManager.AppSettings["ida:TenantGuid"]; private static readonly string ClientId = System.Configuration.ConfigurationManager.AppSettings["ida:ClientID"]; private static readonly string Password = System.Configuration.ConfigurationManager.AppSettings["ida:Password"]; // client secret,valid for one or two years private static readonly string ResourceId = System.Configuration.ConfigurationManager.AppSettings["ida:ResourceID"]; // GET: api/Wolf public async Task<IEnumerable<Wolf>> Get() { AuthenticationResponse authenticationResponse = await GetAuthenticationResponse(); String result = await DoSomeDataOperations(authenticationResponse); return new Wolf[] { new Wolf() { Id = 1,Name = result } }; } private static async Task<AuthenticationResponse> GetAuthenticationResponse() { //https://samlman.wordpress.com/2015/06/04/getting-an-azure-access-token-for-a-web-application-entirely-in-code/ //create the collection of values to send to the POST List<KeyValuePair<string,string>> vals = new List<KeyValuePair<string,string>>(); vals.Add(new KeyValuePair<string,string>("grant_type","client_credentials")); vals.Add(new KeyValuePair<string,string>("resource",ResourceId)); vals.Add(new KeyValuePair<string,string>("client_id",ClientId)); vals.Add(new KeyValuePair<string,string>("client_secret",Password)); vals.Add(new KeyValuePair<string,string>("username","someUser@someTenant.onmicrosoft.com")); vals.Add(new KeyValuePair<string,string>("password","xxxxxx")); //create the post Url string url = string.Format("https://login.microsoftonline.com/{0}/oauth2/token",TenantGuid); //make the request HttpClient hc = new HttpClient(); //form encode the data we’re going to POST HttpContent content = new FormUrlEncodedContent(vals); //plug in the post body HttpResponseMessage hrm = hc.PostAsync(url,content).Result; AuthenticationResponse authenticationResponse = null; if (hrm.IsSuccessStatusCode) { //get the stream Stream data = await hrm.Content.ReadAsStreamAsync(); DataContractJsonSerializer serializer = new DataContractJsonSerializer(typeof (AuthenticationResponse)); authenticationResponse = (AuthenticationResponse) serializer.ReadObject(data); } else { authenticationResponse = new AuthenticationResponse() {ErrorMessage = hrm.StatusCode +" "+hrm.RequestMessage}; } return authenticationResponse; } private static async Task<String> DoSomeDataOperations(AuthenticationResponse authResult) { if (authResult.ErrorMessage != null) { return "problem getting AuthToken: " + authResult.ErrorMessage; } using (HttpClient httpClient = new HttpClient()) { httpClient.BaseAddress = new Uri(ResourceId); httpClient.Timeout = new TimeSpan(0,authResult.access_token); //Retreive Entity///////////////////////////////////////////////////////////////////////////////////// var retrieveResponse = await httpClient.GetAsync("/api/data/v8.0/feedback?$select=title,rating&$top=10"); //var retrieveResponse = await httpClient.GetAsync("/api/data/v8.0/$metadata"); if (!retrieveResponse.IsSuccessStatusCode) { return retrieveResponse.ReasonPhrase; } return "it worked!"; } } 解决方法
我终于找到了解决方案.由Joao R.在这篇文章中提供:
https://community.dynamics.com/crm/f/117/t/193506 首先:忘记ADAL 我的问题是我一直使用“错误的”URL,因为在不使用Adal(或更一般的用户重定向)时,您似乎需要其他地址. 解 为令牌构造以下HTTP-Reqest: 网址: 标题: >缓存控制:无缓存 身体: > client_id:YourClientIdFromAzureAd 构造以下HTTP-Request以访问WebApi: 网址:https://MyCompanyTenant.api.crm.dynamics.com/api/data/v8.0/accounts 标题: >缓存控制:无缓存 Node.js解决方案(获取令牌的模块) var https = require("https"); var querystring = require("querystring"); var config = require("../config/configuration.js"); var q = require("q"); var authHost = config.oauth.host; var authPath = config.oauth.path; var clientId = config.app.clientId; var resourceId = config.crm.resourceId; var username = config.crm.serviceUser.name; var password = config.crm.serviceUser.password; var clientSecret =config.app.clientSecret; function retrieveToken() { var deferred = q.defer(); var bodyDataString = querystring.stringify({ grant_type: "password",client_id: clientId,resource: resourceId,username: username,password: password,client_secret: clientSecret }); var options = { host: authHost,path: authPath,method: 'POST',headers: { "Content-Type": "application/x-www-form-urlencoded","Cache-Control": "no-cache" } }; var request = https.request(options,function(response){ // Continuously update stream with data var body = ''; response.on('data',function(d) { body += d; }); response.on('end',function() { var parsed = JSON.parse(body); //todo: try/catch deferred.resolve(parsed.access_token); }); }); request.on('error',function(e) { console.log(e.message); deferred.reject("authProvider.retrieveToken: Error retrieving the authToken: rn"+e.message); }); request.end(bodyDataString); return deferred.promise; } module.exports = {retrieveToken: retrieveToken}; C#-Solution(获取和使用令牌) public class AuthenticationResponse { public string token_type { get; set; } public string scope { get; set; } public int expires_in { get; set; } public int expires_on { get; set; } public int not_before { get; set; } public string resource { get; set; } public string access_token { get; set; } public string refresh_token { get; set; } public string id_token { get; set; } } private static async Task<AuthenticationResponse> GetAuthenticationResponse() { List<KeyValuePair<string,string>>(); vals.Add(new KeyValuePair<string,ClientId)); vals.Add(new KeyValuePair<string,ResourceId)); vals.Add(new KeyValuePair<string,"yxcyxc@xyxc.onmicrosoft.com")); vals.Add(new KeyValuePair<string,"yxcycx")); vals.Add(new KeyValuePair<string,"password")); vals.Add(new KeyValuePair<string,Password)); string url = string.Format("https://login.windows.net/{0}/oauth2/token",Tenant); using (HttpClient httpClient = new HttpClient()) { httpClient.DefaultRequestHeaders.Add("Cache-Control","no-cache"); HttpContent content = new FormUrlEncodedContent(vals); HttpResponseMessage hrm = httpClient.PostAsync(url,content).Result; AuthenticationResponse authenticationResponse = null; if (hrm.IsSuccessStatusCode) { Stream data = await hrm.Content.ReadAsStreamAsync(); DataContractJsonSerializer serializer = new DataContractJsonSerializer(typeof(AuthenticationResponse)); authenticationResponse = (AuthenticationResponse)serializer.ReadObject(data); } return authenticationResponse; } } private static async Task DataOperations(AuthenticationResponse authResult) { using (HttpClient httpClient = new HttpClient()) { httpClient.BaseAddress = new Uri(ResourceApiId); httpClient.Timeout = new TimeSpan(0,0); //2 minutes httpClient.DefaultRequestHeaders.Add("OData-MaxVersion","4.0"); httpClient.DefaultRequestHeaders.Add("OData-Version","4.0"); httpClient.DefaultRequestHeaders.Add("Cache-Control","no-cache"); httpClient.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json")); httpClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer",authResult.access_token); Account account = new Account(); account.name = "Test Account"; account.telephone1 = "555-555"; string content = String.Empty; content = JsonConvert.SerializeObject(account,new JsonSerializerSettings() { DefaultValueHandling = DefaultValueHandling.Ignore }); HttpRequestMessage request = new HttpRequestMessage(HttpMethod.Post,"api/data/v8.0/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 '{0}' created.",account.name); } else { throw new Exception(String.Format("Failed to create account '{0}',response.ReasonPhrase)); } (...) (编辑:李大同) 【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容! |