【问题标题】:WebAPI CORS with Windows Authentication - allow Anonymous OPTIONS request带有 Windows 身份验证的 WebAPI CORS - 允许匿名 OPTIONS 请求
【发布时间】:2015-02-09 10:38:12
【问题描述】:

我有一个使用 Windows 身份验证运行的 WebAPI 2 REST 服务。它与网站分开托管,因此我使用 ASP.NET CORS NuGet 包启用了 CORS。我的客户网站正在使用 AngularJS。

到目前为止,这是我所经历的:

  1. 我没有设置 withCredentials,因此 CORS 请求返回 401。通过将 withCredentials 添加到我的 $httpProvider 配置中解决。
  2. 接下来,我使用通配符来源设置了 EnableCorsAttribute,这在使用凭据时是不允许的。通过设置明确的来源列表来解决。
  3. 这使我的 GET 请求成功,但我的 POST 发出了预检请求,并且我没有创建任何控制器操作来支持 OPTIONS 动词。为了解决这个问题,我将 MessageHandler 实现为全局 OPTIONS 处理程序。它只是为任何 OPTIONS 请求返回 200。我知道这并不完美,但目前在 Fiddler 中有效。

我被困在哪里 - 我的 Angular 预检调用不包括凭据。根据this answer,这是设计使然,因为 OPTIONS 请求被设计为匿名的。但是,Windows 身份验证使用 401 停止请求。

我尝试将 [AllowAnonymous] 属性放在我的 MessageHandler 上。在我的开发计算机上,它可以工作 - OPTIONS 动词不需要身份验证,但其他动词需要。但是,当我构建并部署到测试服务器时,我的 OPTIONS 请求继续收到 401。

在使用 Windows 身份验证时,是否可以在我的 MessageHandler 上应用 [AllowAnonymous]?如果是这样,有关如何执行此操作的任何指导?或者这是错误的兔子洞,我应该寻找不同的方法?

更新: 我能够通过在 IIS 中的站点上同时设置 Windows 身份验证和匿名身份验证来使其工作。这导致一切都允许匿名,所以我添加了一个全局过滤器 Authorize,同时在我的 MessageHandler 上保留 AllowAnonymous。

但是,这感觉像是一种 hack……我一直都明白,应该只使用一种身份验证方法(不能混合使用)。如果有人有更好的方法,我将不胜感激。

【问题讨论】:

  • 您可能应该添加诸如“selfhost”或“owin”之类的标签,因为这与 IIS 之类的东西无关。 :)
  • 我使用了本指南codeproject.com/Articles/1119206/…(2016 年发布),与下面的大多数答案非常相似

标签: angularjs iis asp.net-web-api cors windows-authentication


【解决方案1】:

我通过 HttpListener 使用自托管,以下解决方案对我有用:

  1. 我允许匿名 OPTIONS 请求
  2. 在 SupportsCredentials 设置为 true 的情况下启用 CORS
var cors = new EnableCorsAttribute("*", "*", "*");
cors.SupportsCredentials = true;
config.EnableCors(cors);
var listener = appBuilder.Properties["System.Net.HttpListener"] as HttpListener;
if (listener != null)
{
    listener.AuthenticationSchemeSelectorDelegate = (request) => {
    if (String.Compare(request.HttpMethod, "OPTIONS", true) == 0)
    {
        return AuthenticationSchemes.Anonymous;
    }
    else
    {
        return AuthenticationSchemes.IntegratedWindowsAuthentication;
    }};
}

【讨论】:

  • 这应该被标记为答案,AuthenticationSchemeSelectorDelegate 正是您需要确保 OPTIONS 请求被全局排除在任何身份验证设置之外。太棒了,这个答案也正是我所需要的。 (我建议将“selfhost”标签添加到问题中)。
  • 如何在 webApiConfig.cs 注册方法中获取 appBuilder 实例?
  • 这个答案的主要问题是问题直接提到他正在使用 IIS。 (我知道它可能是后来添加的,但仍然。更新了问题标签...)
【解决方案2】:

我一直在努力使 CORS 请求在以下限制内工作(与 OP 的非常相似):

  • 所有用户的 Windows 身份验证
  • 不允许匿名身份验证
  • 与 IE11 一起工作,in some cases 不发送 CORS 预检请求(或至少不作为 OPTIONS 请求到达 global.asax BeginRequest)

我的最终配置如下:

web.config - 允许未经身份验证(匿名)的预检请求 (OPTIONS)

<system.web>
    <authentication mode="Windows" />
    <authorization>
        <allow verbs="OPTIONS" users="*"/>
        <deny users="?" />
    </authorization>
</system.web>

global.asax.cs - 正确回复允许来自另一个域的调用者接收数据的标头

protected void Application_AuthenticateRequest(object sender, EventArgs e)
{
    if (Context.Request.HttpMethod == "OPTIONS")
    {
        if (Context.Request.Headers["Origin"] != null)
            Context.Response.AddHeader("Access-Control-Allow-Origin", Context.Request.Headers["Origin"]);

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

        Response.End();
    }
}

CORS 启用

public static class WebApiConfig
{
    public static void Register(HttpConfiguration config)
    {
        // all requests are enabled in this example. SupportsCredentials must be here to allow authenticated requests          
        var corsAttr = new EnableCorsAttribute("*", "*", "*") { SupportsCredentials = true };
        config.EnableCors(corsAttr);
    }
}

protected void Application_Start()
{
    GlobalConfiguration.Configure(WebApiConfig.Register);
}

【讨论】:

  • 这有帮助!我最终没有将授权元素添加到 web.config 中,它仍然有效。我相信 Application_AuthenticateRequest 覆盖消除了在调用 OPTION 方法时允许进行身份验证的需要。
  • @MadMoai - 我会在没有授权的情况下尝试,尽管我认为在 IIS 中仅启用 Windows 身份验证并且必须为未经身份验证的请求启用选项时需要它。谢谢。
  • 根据Microsoft,CORS 规范指出,如果 SupportsCredentials 为 true,则将来源设置为“*”是无效的。
  • @WorkSmarter 这是真的,但事实证明,WebAPI 2 (Microsoft.AspNet.WebApi.Cors.5.2.7) 的当前版本的 CORS 库不会使用此设置返回“Access-Control-Allow-Origin: *”,而是使用请求的 Origin 标头中的值...
【解决方案3】:

这是一个更简单的解决方案——几行代码允许所有“OPTIONS”请求有效地模拟应用程序池帐户。您可以关闭匿名,并按照常规做法配置 CORS 策略,然后将以下内容添加到您的 global.asax.cs:

            protected void Application_AuthenticateRequest(object sender, EventArgs e)
            {
                if (Context.Request.HttpMethod == "OPTIONS" && Context.User == null)
                {
                    Context.User = System.Security.Principal.WindowsPrincipal.Current;
                }
            }

【讨论】:

    【解决方案4】:

    在我们的情况下:

    • Windows 身份验证
    • 多个 CORS 来源
    • SupportCredentials 设置为 true
    • IIS 托管

    我们发现解决方案在别处:

    在 Web.Config 中,我们只需添加 runAllManagedModulesForAllRequests=true

    <modules runAllManagedModulesForAllRequests="true">
    

    我们通过研究为什么没有触发 Application_BeginRequest 的解决方案最终得出了这个解决方案。

    我们拥有的其他配置:

    在Web.Config

    中
        <authentication mode="Windows" />
        <authorization>
          <allow verbs="OPTIONS" users="*" />
          <deny users="?"/>
        </authorization>
    

    在 WebApiConfig

            private static string GetAllowedOrigins()
            {
                return ConfigurationManager.AppSettings["CorsOriginsKey"];
            }
    
            public static void Register(HttpConfiguration config)
            {
                //set cors origins
                string origins = GetAllowedOrigins();
                var cors = new EnableCorsAttribute(origins, "*", "*");
                config.EnableCors(cors);
    
                config.Routes.MapHttpRoute(
                    name: "DefaultApi",
                    routeTemplate: "api/{controller}/{action}/{id}",
                    defaults: new { id = RouteParameter.Optional }
                );
           }
    
    

    BTW "*" cors origin 与 Windows Authentication / SupportCredentials = true 不兼容

    https://docs.microsoft.com/en-us/aspnet/web-api/overview/security/enabling-cross-origin-requests-in-web-api#pass-credentials-in-cross-origin-requests

    【讨论】:

      【解决方案5】:

      我以非常相似的方式解决了它,但有一些细节并专注于 oData 服务

      我没有在 IIS 中禁用匿名身份验证,因为我需要它来发布请求

      我在 Global.aspx 中添加了与上面相同的代码(在 Access-Control-Allow-Headers 中添加 MaxDataServiceVersion)

      protected void Application_BeginRequest(object sender, EventArgs e)
      {
          if ((Context.Request.Path.Contains("api/") || Context.Request.Path.Contains("odata/")) && Context.Request.HttpMethod == "OPTIONS")
          {
              Context.Response.AddHeader("Access-Control-Allow-Origin", Context.Request.Headers["Origin"]);
              Context.Response.AddHeader("Access-Control-Allow-Headers", "Origin, X-Requested-With, Content-Type, Accept,MaxDataServiceVersion");
              Context.Response.AddHeader("Access-Control-Allow-Methods", "GET, POST, PUT, DELETE, OPTIONS");
              Context.Response.AddHeader("Access-Control-Allow-Credentials", "true");
              Context.Response.End();
          }
      } 
      

      和WebAPIConfig.cs

      public static void Register(HttpConfiguration config)
      {
         // Web API configuration and services
         var cors = new EnableCorsAttribute("*", "*", "*");
         cors.SupportsCredentials = true;
         config.EnableCors(cors);
      
      
         config.Routes.MapHttpRoute(
             name: "DefaultApi",
             routeTemplate: "api/{controller}/{id}",
             defaults: new { id = RouteParameter.Optional }
         );
      }
      

      和 AngularJS 调用

      $http({
             method: 'POST',
              url: 'http://XX.XXX.XXX.XX/oData/myoDataWS.svc/entityName',
              withCredentials: true,
              headers: {
                  'Content-Type': 'application/json;odata=verbose',
                  'Accept': 'application/json;odata=light;q=1,application/json;odata=verbose;q=0.5',
                  'MaxDataServiceVersion': '3.0'
              },
              data: {
                  '@odata.type':'entityName',
                  'field1': 1560,
                  'field2': 24,
                  'field3': 'sjhdjshdjsd',
                  'field4':'wewewew',
                  'field5':'ewewewe',
                  'lastModifiedDate':'2015-10-26T11:45:00',
                  'field6':'1359',
                  'field7':'5'
              }
          });
      

      【讨论】:

        【解决方案6】:

        戴夫,

        在玩弄了 CORS 包之后,这就是它对我有用的原因:[EnableCors(origins: "", headers: "", methods: "*", SupportsCredentials=true)]

        我必须启用 SupportsCredentials=true。 Origins、Headers 和 Methods 都设置为“*”

        【讨论】:

        • 我从一开始就有这个设置,否则服务器不会接受凭据头。不过,感谢您的尝试。
        • asp.net/web-api/overview/security/… 部分:在跨域请求中传递凭据 CORS 规范还规定,如果 SupportsCredentials 为 true,则将源设置为“”是无效的。您应该提供您想要允许的来源,而不是使用“”
        【解决方案7】:

        如果不需要,请在 IIS 中禁用匿名身份验证。

        在你的全局 asax 中添加这个:

        protected void Application_BeginRequest(object sender, EventArgs e)
        {
            if ((Context.Request.Path.Contains("api/") || Context.Request.Path.Contains("odata/")) && Context.Request.HttpMethod == "OPTIONS")
            {
                Context.Response.AddHeader("Access-Control-Allow-Origin", Context.Request.Headers["Origin"]);
                Context.Response.AddHeader("Access-Control-Allow-Headers", "Origin, X-Requested-With, Content-Type, Accept");
                Context.Response.AddHeader("Access-Control-Allow-Methods", "GET, POST, PUT, DELETE, OPTIONS");
                Context.Response.AddHeader("Access-Control-Allow-Credentials", "true");
                Context.Response.End();
            }
        } 
        

        确保您在启用 cors 的位置也启用了凭证使用,例如:

        public static void Register(HttpConfiguration config)
        {
           // Web API configuration and services
           var cors = new EnableCorsAttribute("*", "*", "*");
           cors.SupportsCredentials = true;
           config.EnableCors(cors);
        
           // Web API routes
           config.MapHttpAttributeRoutes();
        
           config.Routes.MapHttpRoute(
               name: "DefaultApi",
               routeTemplate: "api/{controller}/{id}",
               defaults: new { id = RouteParameter.Optional }
           );
        }
        

        如您所见,我在全局范围内启用了 CORS,并使用应用程序 BeginRequest 挂钩对 api (Web Api) 的所有 OPTIONS 请求和 odata 请求(如果您使用它)进行身份验证。

        这适用于所有浏览器,在客户端记得添加 xhrFiled withCredentials,如下所示。

        $.ajax({
            type : method,
            url : apiUrl,
            dataType : "json",
            xhrFields: {
                withCredentials: true
            },
            async : true,
            crossDomain : true,
            contentType : "application/json",
            data: data ? JSON.stringify(data) : ''
        }).....
        

        我正在尝试寻找另一种避免使用钩子的解决方案,但直到现在都没有成功, 我会使用 web.config 配置来执行以下操作: 警告下面的配置不起作用!

          <system.web>
            <compilation debug="true" targetFramework="4.5" />
            <httpRuntime targetFramework="4.5" />
            <authentication mode="Windows" />
            <authorization>
              <deny verbs="GET,PUT,POST" users="?" />
              <allow verbs="OPTIONS" users="?"/>
            </authorization>
          </system.web>
          <location path="api">
            <system.web>
              <authorization>
                <allow users="?"/>
              </authorization>
            </system.web>
          </location>
        

        【讨论】:

          【解决方案8】:

          我在网上找到的其他解决方案对我不起作用或看起来太老套了;最后我想出了一个更简单且可行的解决方案:

          web.config:

          <system.web>
              ...
              <authentication mode="Windows" />
              <authorization>
                  <deny users="?" />
              </authorization>
          </system.web>
          

          项目属性:

          1. 开启Windows Authentication
          2. 关闭Anonymous Authentication

          设置 CORS:

          [assembly: OwinStartup(typeof(Startup))]
          namespace MyWebsite
          {
              public class Startup
              {
                  public void Configuration(IAppBuilder app)
                  {
                      app.UseCors(CorsOptions.AllowAll);
          

          这需要 NUget 上可用的 Microsoft.Owin.Cors 程序集。

          角度初始化:

          $httpProvider.defaults.withCredentials = true;
          

          【讨论】:

            【解决方案9】:

            这是我的解决方案。

            Global.asax*

            protected void Application_BeginRequest(object sender, EventArgs e)
            {
                if(!ListOfAuthorizedOrigins.Contains(Context.Request.Headers["Origin"])) return;
            
                if (Request.HttpMethod == "OPTIONS")
                {
                    HttpContext.Current.Response.Headers.Remove("Access-Control-Allow-Origin");
                    HttpContext.Current.Response.AddHeader("Access-Control-Allow-Origin", Context.Request.Headers["Origin"]);
                    HttpContext.Current.Response.StatusCode = 200;
                    HttpContext.Current.Response.End();
                }
            
                if (Request.Headers.AllKeys.Contains("Origin"))
                {
                    HttpContext.Current.Response.Headers.Remove("Access-Control-Allow-Origin");
                    HttpContext.Current.Response.AddHeader("Access-Control-Allow-Origin", Context.Request.Headers["Origin"]);
                }
            }
            

            【讨论】:

              猜你喜欢
              • 2023-04-09
              • 2018-04-30
              • 2014-01-15
              • 2013-06-09
              • 2020-08-11
              • 2021-03-23
              • 1970-01-01
              • 2018-01-07
              • 2016-11-07
              相关资源
              最近更新 更多