【问题标题】:Unauthorized HTTP request to Dynamics CRM Web API via Angular 2 using ADAL使用 ADAL 通过 Angular 2 向 Dynamics CRM Web API 发出未经授权的 HTTP 请求
【发布时间】:2017-09-08 09:14:17
【问题描述】:

我正在尝试使用 Angular 创建一个可以通过 Web API 连接到 Dynamics CRM 的应用程序。这些是我遵循的步骤:

1. 在 Azure 中注册本机应用程序,授予所需的委派权限并更新清单以允许隐式流。

2. 在 CRM 中创建了一个应用程序用户,将其应用程序 ID 设置为我的 Azure 注册应用程序的客户端 ID。为我的应用程序用户分配了一个自定义安全角色。

3. 克隆了许多通过 ADAL 向 Azure AD 进行身份验证的 Angular 2 快速入门 Git 存储库,例如 this one

4.更新了克隆代码的adal配置,设置了我的tenantclientIdredirectUriendpoints

到目前为止,这已经成功。我能够以我的应用程序用户或作为 Azure AD 一部分的其他 CRM 用户的身份通过我的浏览器启动应用程序并登录。这将返回一个令牌。

5. 尝试向v8.0v8.2 发送http.get(我听说v8.2 不支持跨域调用):

getEntities(): Promise<any> {
    let token = this.adalService.getCachedToken(this.adalService.config.clientId);
    let headers = new Headers({
        'Authentication': 'Bearer ' + token,
        'Accept': 'application/json',
        'Content-Type': 'application/json; charset=utf-8',
        'OData-MaxVersion': '4.0',
        'OData-Version': '4.0'
    });
    let options = new RequestOptions({ headers: headers });

    return this.http.get(`${crmURL}/api/data/v8.2/accounts`, options)
        .toPromise()
        .then((res) => { return res; })
        .catch((e) => { console.error(e); });
}

6.收到此错误消息:

上面写着:

No 'Access-Control-Allow-Origin' header is present on the requested resource. 
Origin 'http://localhost:3000' is therefore not allowed access. 
The response had HTTP status code 401.

查看我的 Chrome 浏览器的网络选项卡,我收到两个响应:

响应 1

常规

Request URL:https://ms-dyn365-prevxxxxxx/api/data/v8.2/accounts
Request Method:OPTIONS
Status Code:200 OK
Remote Address:104.44.xxx.xxx:xxx
Referrer Policy:no-referrer-when-downgrade

标题

Access-Control-Allow-Headers:authentication,content-type,odata-maxversion,odata-version
Access-Control-Allow-Methods:GET
Access-Control-Allow-Origin:http://localhost:3000
Access-Control-Expose-Headers:Preference-Applied,OData-EntityId,Location,ETag,OData-Version,Content-Encoding,Transfer-Encoding,Content-Length,Retry-After
Access-Control-Max-Age:3600
Content-Length:0
Date:Thu, 13 Apr 2017 10:08:01 GMT
Server:Microsoft-IIS/8.5
Set-Cookie:crmf5cookie=!NDyiupL55lrWWLtPQKTK52dwbxk9wdEAHeCiec0/z/7x9KWXe2dVIdQCGvL0S/HAp7F3N0OGfeWf/70=;secure; path=/
Strict-Transport-Security:max-age=31536000; includeSubDomains
Vary:Origin
X-Powered-By:ASP.NET

响应 2

常规

Request URL:https://ms-dyn365-prevxxxxx.crm4.dynamics.com/api/data/v8.2/accounts
Request Method:GET
Status Code:401 Unauthorized
Remote Address:104.xx.xxx.xxx:xxx
Referrer Policy:no-referrer-when-downgrade

标题

Cache-Control:private
Content-Length:49
Content-Type:text/html
Date:Thu, 13 Apr 2017 10:08:01 GMT
REQ_ID:b2be65bc-xxxx-4b34-xxxx-5c39812650xx
Server:Microsoft-IIS/8.5
Set-Cookie:ReqClientId=xxxxxxxx-70b5-45f9-9b84-30f59481bxxx; expires=Wed, 13-Apr-2067 10:08:01 GMT; path=/; secure; HttpOnly
Strict-Transport-Security:max-age=31536000; includeSubDomains
WWW-Authenticate:Bearer authorization_uri=https://login.windows.net/xxxxxxxx-87e4-4d81-8010-xxxxxxxxxxxxx/oauth2/authorize, resource_id=https://ms-dyn365-prevxxxxxx.crm4.dynamics.com/
X-Powered-By:ASP.NET

注意:我可以通过 Postman 成功访问 Web API:

1.我在 Azure 中为我的应用程序输入 https://www.getpostman.com/oauth2/callback 作为回调 URL。

2.我打开Postman,设置参数如下,按Request Token

Token Name: Token
Auth URL: https://login.windows.net/common/oauth2/authorize?resource=https://ms-dyn365-prevxxxxxx.crm4.dynamics.com
Access Token URL: https://login.microsoftonline.com/common/oauth2/token
Client ID: xxxxxxxx-ebd3-429c-9a95-xxxxxxxxxxxx
Callback URL: https://www.getpostman.com/oauth2/callback
Grant Type: Authorization Code

3.这会打开一个我登录的网页。

4.返回一个令牌,我将其添加到 Postman GET 标头中:

Content-Type: application/json
Authorization: Bearer eyJ0eXAiO...

5. 在 Postman 中发送 GET:

GET https://ms-dyn365-prevxxxxxx.crm4.dynamics.com/api/data/v8.2/accounts

6.帐户已成功返回。

如果我在我的应用程序中使用相同的令牌,我仍然会收到 401 错误。

【问题讨论】:

  • 在 Azure 中注册应用时,是否将“localhost:3000”设置为“登录 URL”?
  • @PawelGradecki 我实际上在 Azure 中看不到“登录 URL”选项(我使用的是较新的 Azure 门户)。我可以看到“主页”和“重定向 URI”都设置为“localhost:3000”。
  • 还有其他查询有效吗?例如,您是否尝试过获取帐户?因为目前您正在查询元数据,所以我的猜测是即使使用 CORS 也可能无法实现(尽管我没有找到任何信息来支持这个假设)
  • 你也应该在你的获取请求中使用“application/json”而不是“application/jsonp”
  • @PawelGradecki 我试过 /api/data/v8.2/accounts 并收到同样的错误。 application/jsonp 是我的问题中的一个错误,我的代码实际上使用了json,对不起!我现在已经编辑了我的问题。

标签: angular azure dynamics-crm azure-active-directory adal


【解决方案1】:

Access-Control-Allow-Origin 表示问题是由跨域引起的。

通常,我们可以使用 JSONP 或在服务器上设置 Access-Control-Allow-Origin 标头。如果 JSONP 和 header 都不能设置,因为服务是第三方提供的,我们还可以创建一个服务代理,允许从特定的原始调用。

更多关于AJAX跨域问题的细节,可以参考这个thread

更新

经进一步排查,该问题为服务器端问题,与特定版本的 REST 请求有关。

8.2 版本的 REST 目前不支持跨域。作为一种解决方法,我们可以使用8.0,这对我来说效果很好,如下图所示:

附加代码demo进行测试:

<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8" />
    <base href="/">
    <title></title>
    <script src="node_modules\angular\angular.js"></script>

</head>
<body>
    <div ng-app="myApp">
        <div ng-controller="HomeController">
            <ul class="nav navbar-nav navbar-right">            
                     <li><a class="btn btn-link" ng-click="listAccounts()">List account info</a></li>
            </ul>
           <div ng-repeat="account in accounts">
                    <span>name:</span><span>{{account.name}}</span>
           </div>
        </div>
    </div>

    <script>
    var myApp = angular.module('myApp',[]);

    myApp.controller('HomeController', ['$scope', '$http',
                            function ($scope, $http){

                                    $scope.listAccounts=function(){

                                        var req = {
                                        method: 'GET',
                                        url: 'https://{domain}.crm.dynamics.com/api/data/v8.0/accounts',
                                        headers: {
                                        'authorization': 'Bearer eyJ0eXAiO...'
                                        }
                                        };
                                       $http(req).then(function(response){
                                            $scope.accounts=response.data.value
                                       }, function(){

                                       });
                                    }
                            }]);
    </script>
</body>
</html>

附加 Angular 2 的测试代码示例:

https://github.com/VitorX/angular2-adaljs-crm

【讨论】:

  • 感谢您的回答。 Access-Control-Allow-Origin 应该在服务器上启用(我无法控制),因为它是托管在 Azure 中的 Dynamics 365。我不想使用 JSONP,因为我知道可以使用 JSON; Microsoft 在他们的示例中这样做了 here
  • @DaveClark 根据您提到的文档,它使用 CORS 作为同源策略。并且您的代码中的标头设置为 JSONP ,请将其修改为您提到的link 提供的代码示例。
  • @DaveClark 该问题与 REST 的特定版本有关,请查看更新,如果有帮助请告诉我。
  • 根据我的理解,问题是服务器端问题,不应该与客户端有关。我正在使用 Office 365 中的 CRM 对其进行测试,Angular 1 和 Angular 2 对我来说都很好。对于这个问题,您可以检查服务器端是否支持 CORS。如果没有,您可以开发一个 Web API 作为代理作为解决方法。
  • @DaveClark 我已经链接了测试代码示例供您参考。该代码示例仅用于测试CORS问题,点击获取消息时无法正常工作,但您可以通过Chrome的newwork选项卡查看请求是否成功。
猜你喜欢
  • 2017-06-06
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2018-07-13
  • 1970-01-01
  • 2018-01-10
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多