asp.net - 让集成安全性和 CORS 与 Asp.Net、Angular 6 和 IIS 配合使用

标签 asp.net cors

我有一个带有 Angular 前端和 Asp.Net Web 服务的应用程序。我正在 Angular 中使用 HttpClient 实体来进行 Web 服务调用。我已经配置了 CORS,并且我的 GET 使用以下基本结构在整个应用程序中完美运行

let url = environment.base_url + 'api/SubDoc/GetDocument?docID=' + docID;

this.httpClient.get(url,
  {
    withCredentials: true
  })
  .subscribe(
    response => {
      console.log(response);
      newData = response;
      this.currentDocument = newData;
    }
  );

我已经证明 CORS 正在工作,因为只要我在对 EnableCors 的调用中正确编辑 Origin 值,我就可以从本地主机 session 或在我们的 QA 和生产服务器上运行的应用程序 session 访问相同的服务端点

        config.EnableCors(new EnableCorsAttribute(origins: "http://localhost:2453,http://localhost:2452,http://***,http://10.100.101.46", headers: headers, methods: methods) { SupportsCredentials = true });

header 和方法定义为以下字符串:

        headers = "Origin, Content-Type, X-Auth-Token, content-type,application/x-www-form-urlencoded";
        methods = "GET,PUT,POST,OPTIONS,DELETE";

但是,我无法使用以下 HttpClient 结构向同一服务发出 PUT 请求

let url = this.base_url + 'api/ContactGroup/Put';
// Try the new HttpClient object
this.httpClient.put(url, body,
  {
    headers: new HttpHeaders({ 'Content-Type': 'application/json' }),
    withCredentials: true
  })
  .subscribe(
    response=>{
      let temp:number;
      temp = parseInt(response.toString(),10);
      if(!isNaN(temp)){
        this.groupID = temp;          
      }
    }
  );

当我从同一服务器连接时(即不适用 CORS),此 PUT 请求可以完美运行,但当我尝试从任何其他服务器连接时,此 PUT 请求会因预检错误而失败。

无法加载http://***/api/ContactGroup/Put:对预检请求的响应未通过访问控制检查:请求的资源上不存在“Access-Control-Allow-Origin” header 。来源'http://localhost:2452 ' 因此不允许访问。

正如我上面所解释的,我确实定义了 Access-Control-Allow-Origin header ,并且我相信我已将 PUT 和 OPTIONS 作为受支持的方法包含在内。

我认为该问题与 PUT 请求未发送凭据有关。 GET 请求的请求 header 中有一个凭据项,但 PUT 没有,即使我使用的是 withCredentials 选项。

这是 PUT 请求的 header :

Request URL: http://reitgc01/REITServicesQA/api/ContactGroup/Put
Request Method: OPTIONS
Status Code: 401 Unauthorized
Remote Address: 10.100.101.46:80
Referrer Policy: no-referrer-when-downgrade
Content-Type: text/html
Provisional headers are shown
Access-Control-Request-Headers: content-type
Access-Control-Request-Method: PUT
DNT: 1
Origin: http://localhost:2452
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/69.0.3497.100 Safari/537.36

我现在已经在这个问题上花了很多天了,如果有任何帮助,我们将不胜感激。

最佳答案

感谢 sideshowbarker 为我指明了正确的方向。我终于找到了我一直困扰的根本问题并实现了解决方案。

基本问题是我们的站点是一个内部站点,它使用集成安全性进行连接,这会导致与 IIS 中的 CORS 和 CORS 支持不兼容。前端是带有 ASP.NET Web 服务的 Angular 6 界面。为了使用集成安全性,您必须禁用匿名身份验证。此外,为了使身份验证正常工作,您必须在请求中传递 {withCredentials:true}。这对于 GET 请求效果很好,因为 CORS 规范不需要这些请求的飞行前选项,并且 GET 请求 header 将具有正确解析所需的凭据 token 。

问题出现在 PUT 请求中。 CORS 需要 PUT 的预检 OPTIONS 请求,但即使您设置了 withCredentials:true 标志,OPTIONS 请求也不包含凭据。这意味着您必须启用匿名身份验证,这将破坏您的集成安全性。 22. 这有点棘手。我无法找到一种方法来仅为选项启用匿名登录。我确实找到了一些 StackOverflow 帖子,声称您可以在 IIS 中执行此操作,但我无法使其工作。我还发现 MS 的帖子指出 IIS 不支持此功能。这意味着您必须为 OPTIONS 请求编写自定义处理程序并生成正确的响应以满足浏览器的要求。关于这个主题有很多帖子,但我发现最好的是 Stu 在这个帖子上的回复 401 response for CORS request in IIS with Windows Auth enabled

通过一个小小的增强,我就能让它为我工作。我需要在系统配置中支持多个域,但如果我在原始字段中放置多个 url,我仍然会收到错误。我使用 Request.Headers 的 Origin 字段来获取当前源并将该值发送回我构建的响应 header 中,如下面的代码所示:

编辑:玩了一段时间后,我意识到原来的解决方案有一个缺陷。我的原始代码没有明确检查来源以确保它是允许的。正如我之前所说,这对于 GET 操作来说是可以的,因为 GET 仍然会失败(而且 OPTIONS 调用对于 GET 来说是可选的),但对于 PUT 来说,客户端将传递 OPTIONS 调用并发出 PUT 请求,并在响应中收到错误。但是,服务器仍会执行 PUT 请求,因为 CORS 是在客户端强制执行的。这将导致在不应该发生的情况下插入或更新数据。解决方案是根据 web.config 中维护的 CORS 源列表显式检查检测到的源,如果请求源无效,则返回 403 状态和空白源值。更新后的代码如下

    protected void Application_BeginRequest(object sender, EventArgs e)
    {
        // Declare local variables to store the CORS settings loaded from the web.config
        String origins;
        String headers;
        String methods;

        // load the CORS settings from the webConfig file
        origins = ConfigurationManager.AppSettings["corsOrigin"];
        headers = ConfigurationManager.AppSettings["corsHeader"];
        methods = ConfigurationManager.AppSettings["corsMethod"];


        if (Request.HttpMethod == "OPTIONS")
        {
            // Get the Origin from the Request header. This should be present for any cross domain requests
            IEnumerable<string> headerValues = Request.Headers.GetValues("Origin");
            var id = headerValues.FirstOrDefault();

            String httpOrigin = null;
            // Assign the origin value to the httpOrigin string.
            foreach (var origin in headerValues)
            {
                httpOrigin = origin;
            }

            // Construct the Access Control headers using the derived origin value and the desired content.
            if (httpOrigin == null) httpOrigin = "*";

            // Check to see if the origin is included in the CORS origin list
            if (origins.IndexOf(httpOrigin) < 0)
            {
                // This has failed the CORS check so send a blank origin list and a 403 status (forbidden)
                HttpContext.Current.Response.AddHeader("Access-Control-Allow-Origin", "");
                HttpContext.Current.Response.StatusCode = 403;
            }
            else
            {
                HttpContext.Current.Response.AddHeader("Access-Control-Allow-Origin", httpOrigin);
                HttpContext.Current.Response.StatusCode = 200;
            }

            HttpContext.Current.Response.AddHeader("Access-Control-Allow-Methods", "GET, POST, PUT, DELETE, OPTIONS");
            HttpContext.Current.Response.AddHeader("Access-Control-Allow-Headers", "Origin, X-Requested-With,content-type, Content-Type, Accept, X-Token");
            HttpContext.Current.Response.AddHeader("Access-Control-Allow-Credentials", "true");


            var httpApplication = sender as HttpApplication;
            httpApplication.CompleteRequest();
        }

    }

此代码已添加到 global.asax.cs 文件中的 Application_BeginRequest 方法中。此外,该方法的签名已修改为包含发送者对象,如下所示: protected void Application_BeginRequest(对象发送者,EventArgs e) { 现在,这将生成对满足 CORS 要求的浏览器 OPTIONS 请求的响应,并且后续 GET 仍将得到验证,因此我认为不会有任何负面的安全影响。

最后一部分是构建您的 GET 和 PUT 以包含凭据信息。请求示例如下所示: 让 url =environment.base_url + 'api/SubDoc/GetDocument?docID=' + docID;

this.httpClient.get(url,
  {
    withCredentials: true
  })
  .subscribe(
    response => {
      console.log(response);
      newData = response;
      this.currentDocument = newData;
    }
  );

body = { "GroupID": groupID, "ContactID": contact.ContactID, "IsActive": isActive }

let url = this.base_url + 'api/ContactGroup/Put';
this.httpClient.put(url, body,
  {
    headers: new HttpHeaders({ 'Content-Type': 'application/json' }),
    withCredentials: true
  })
  .subscribe(
    response=>{
      let temp:number;
      temp = parseInt(response.toString(),10);
      if(!isNaN(temp)){
        this.groupID = temp;          
      }
    }
  );

希望这能帮助其他人免去我在尝试让 CORS 与 Angular 6、ASP.NET 和集成安全性配合使用时所经历的痛苦和沮丧

关于asp.net - 让集成安全性和 CORS 与 Asp.Net、Angular 6 和 IIS 配合使用,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/52995906/

相关文章:

asp.net - ExtendedMembershipProvider.GetUserIDfromOauth 返回什么用户 ID?

css - 无法更改文本框的大小

javascript - 将 cookie 域设置为 IP 地址(使用 CORS)

c# - 如何在 asp.net core 3.1 中为每种类型的请求启用 Cors

javascript - node.js express,一个非常奇怪的行为

c# - 动态添加的 LinkBut​​ton CommandName/CommandArgument 设置被删除

c# - 在 LINQ 查询中使用 ToString()?

c# - 部分/部分中的 asp.net mvs 部分?

java - 在 dropwizard 0.7.0 中使用 CORS header 过滤器

reactjs - Ktor 后端的 CORS 问题