带有Windows身份验证的WebAPI CORS - 允许匿名选项请求

时间:2014-12-11 02:37:57

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

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

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

  1. 我没有设置问题,因此CORS请求返回401.通过在我的$ httpProvider配置中添加withCredentials解决。
  2. 接下来,我使用通配符源设置了我的EnableCorsAttribute,使用凭据时不允许这样做。通过设置明确的起源列表解决。
  3. 这使我的GET请求成功,但我的POST发出了预检请求,我还没有创建任何控制器动作来支持OPTIONS动词。为了解决这个问题,我将MessageHandler实现为全局OPTIONS处理程序。它只是为任何OPTIONS请求返回200。我知道这不是完美的,但现在在Fiddler工作。
  4. 我被困的地方 - 我的Angular预检电话不包括凭据。根据{{​​3}},这是设计的,因为OPTIONS请求被设计为匿名的。但是,Windows身份验证使用401停止请求。

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

    使用Windows身份验证时,是否可以在MessageHandler上应用[AllowAnonymous]?如果是的话,有关如何做的任何指导?或者这是一个错误的兔子洞,我应该看一个不同的方法?

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

    然而,这感觉就像一个黑客...我一直都明白只应该使用一种身份验证方法(不混合)。如果有人有更好的方法,我很高兴听到它。

9 个答案:

答案 0 :(得分:13)

我使用HttpListener进行自托管,以下解决方案为我工作:

  1. 我允许匿名选项请求
  2. 启用CORS,SupportsCredentials设置为true
  3. 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;
        }};
    }
    

答案 1 :(得分:6)

我一直在努力使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);
}

答案 2 :(得分:2)

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

我没有禁用IIS中的匿名身份验证,因为我需要POST请求

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

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'
        }
    });

答案 3 :(得分:2)

这是一个更简单的解决方案 - 几行代码允许所有“OPTIONS”请求有效地模拟应用程序池帐户。您可以保持Anonymous关闭,并按照常规做法配置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 :(得分:0)

戴夫,

在使用CORS软件包之后,这就是它对我有用的原因:[EnableCors(起源:“”,标题:“”,方法:“*”, SupportsCredentials =真)]

我必须启用SupportsCredentials = true。起源,标题和方法都设置为“*”

答案 5 :(得分:0)

如果您不需要,请在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)和odata请求(如果您使用它)的所有OPTIONS请求。

这适用于所有浏览器,在客户端,请记得添加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>

答案 6 :(得分:0)

我在网上找到的其他解决方案对我来说没有用,或者看起来太苛刻了;最后,我提出了一个更简单,更有效的解决方案:

<强>的web.config:

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

项目属性:

  1. 开启Windows Authentication
  2. 关闭Anonymous Authentication
  3. 设置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;
    

答案 7 :(得分:0)

这是我的解决方案。

<强>的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"]);
    }
}

答案 8 :(得分:0)

在我们的情况下:

  • 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来源与Windows身份验证不兼容/ 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