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:
网址:
https://login.windows.net/MyCompanyTenant.onmicrosoft.com/oauth2/token
标题:
>缓存控制:无缓存
>内容类型:application / x-www-form-urlencoded
身体:
> clIEnt_ID:YourClIEntIDFromAzureAd
>资源:https://myCompanyTenant.crm.dynamics.com
> username:yourServiceUser@myCompanyTenant.onmicrosoft.com
>密码:yourServiceUserPassword
> grant_type:密码
> clIEnt_secret:YourClIEntSecretFromAzureAd
构造以下http-Request以访问WebAPI:
网址:https://MyCompanyTenant.api.crm.dynamics.com/api/data/v8.0/accounts
标题:
>缓存控制:无缓存
>接受:application / Json
> OData版本:4.0
>授权:Bearer TokenRetrIEvedFomrequestAbove
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: \r\n"+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)); }(...)总结
以上是内存溢出为你收集整理的使用ADAL C#作为机密用户/守护程序服务器/服务器到服务器 – 401未经授权全部内容,希望文章能够帮你解决使用ADAL C#作为机密用户/守护程序服务器/服务器到服务器 – 401未经授权所遇到的程序开发问题。
如果觉得内存溢出网站内容还不错,欢迎将内存溢出网站推荐给程序员好友。
欢迎分享,转载请注明来源:内存溢出
评论列表(0条)