处理在IdentityServer3中的CORS之前发送的飞行前OPTIONS请求

时间:2017-11-01 08:48:11

标签: c# asp.net cors asp.net-web-api2 identityserver3

我们目前正与两个不同的RestAPI团队合作。一个是我们的主系统,另一个是在其中注册IdentityServer的空主机。我们已在主应用中成功配置CORS和行前OPTIONS处理,但现在我们正在努力使用IdentityServer中的OPTIONSCORS已启用并正常工作)。不幸的是,我们在令牌请求中有一些特定的标头,浏览器正在发送行前OPTIONS请求。每次我们这样做,我们得到:

The requested resource does not support HTTP method 'OPTIONS'.

在我们的RestAPI应用程序管道中,我们只需要创建DelegatingHandler

public class OptionsHttpMessageHandler : DelegatingHandler
{
    protected override Task<HttpResponseMessage> SendAsync(
        HttpRequestMessage request,
        CancellationToken cancellationToken)
    {
        if (request.Method == HttpMethod.Options)
        {
            var apiExplorer = GlobalConfiguration.Configuration.Services.GetApiExplorer();

            var controllerRequested = request.GetRouteData().Values["controller"] as string;
            var supportedMethods = apiExplorer.ApiDescriptions
                .Where(d =>
                    {
                        var controller = d.ActionDescriptor.ControllerDescriptor.ControllerName;
                        return string.Equals(
                            controller, controllerRequested, StringComparison.OrdinalIgnoreCase);
                    })
                .Select(d => d.HttpMethod.Method)
                .Distinct();

            if (!supportedMethods.Any())
            {
                return Task.Factory.StartNew(
                    () => request.CreateResponse(HttpStatusCode.NotFound));
            }

            return Task.Factory.StartNew(() =>
            {
                var response = new HttpResponseMessage(HttpStatusCode.OK);

                return response;
            });
        }

        return base.SendAsync(request, cancellationToken);
    }
}

并在Global.asax注册:

GlobalConfiguration.Configuration.MessageHandlers.Add(new OptionsHttpMessageHandler());

我不知道如何在IdentityServer管道中注册它。这是Owin Startup类的简化实现:

[assembly: OwinStartup(typeof(MyNamespace.IdentityServer.Host.Startup))]
namespace MyNamespace.IdentityServer.Host
{
    public class Startup
    {
        public void Configuration(IAppBuilder app)
        {
            var identityServerServiceFactory = new IdentityServerServiceFactory()
                .UseInMemoryScopes(Scopes.Get())
                .UseInMemoryClients(Clients.Get());

            identityServerServiceFactory.UserService = new Registration<IUserService>(resolver => UserServiceFactory.Create());

            app.Map("/identity", idsrvApp =>
            {
                idsrvApp.UseIdentityServer(new IdentityServerOptions
                {
                    SiteName = "MyNamespace.IdentityServer",
                    SigningCertificate = this.LoadCertificate(),
                    Factory = identityServerServiceFactory
                });
            });
        }
    }
}

到目前为止已经尝试过:

  1. 在web.config

    中的<handlers>部分设置一些ISS处理程序
  2. 我不记得我在哪里找到了这个解决方案,但有人建议你做以下事情:

    • 在IdentityServer之前注册主机API
    • 创建一个控制器,路由集设置为我们想要劫持OPTIONS请求的地址(在我的情况下为/identity/connect/token
    • 添加[HttpOptions]标记的操作,返回200, OK

    ...但不幸的是,正如我所料,我的新控制器劫持了IdentityServer的所有流量,而不仅仅是OPTIONS个请求。

  3. 更新 - 忘记提及第三个选项

    1. 我提出了一个有效的解决方案(我的意思是因为我还没有完成)。这很简单,但我宁愿避免使用这种方法,因为它增加了额外的工作,以及稍后要维护的代码。我们的想法是简单地将IdentityServer's标记端点包装在我们自己的端点中,这样AJAX只调用我们的RestAPI。而且,正如我之前所说,我们的主机已经设置为CORS并且在飞行前OPTIONS

1 个答案:

答案 0 :(得分:0)

我设法克服了这个问题。 我希望这只是暂时的解决方案,因为它不优雅也不安全(我认为它可能为某种攻击开辟道路,但我是安全的初学者)。

我仍在等待更好的解决方案,并且我愿意更改已接受的答案。

解决方案是由Chtiwi Malek在另一个SO question中提出的,我也在我的问题中提供了这个解决方案(我的错,我没有检查所有答案)。

我们所要做的就是在每个请求的开头处理OPTIONS

protected void Application_BeginRequest()
{
    var res = HttpContext.Current.Response;
    var req = HttpContext.Current.Request;
    res.AppendHeader("Access-Control-Allow-Origin", "*");
    res.AppendHeader("Access-Control-Allow-Credentials", "true");
    res.AppendHeader("Access-Control-Allow-Headers", "Content-Type, X-CSRF-Token, X-Requested-With, Accept, Accept-Version, Content-Length, Content-MD5, Date, X-Api-Version, X-File-Name");
    res.AppendHeader("Access-Control-Allow-Methods", "POST,GET,PUT,PATCH,DELETE,OPTIONS");

    if (req.HttpMethod == "OPTIONS")
    {
        res.StatusCode = 200;
        res.End();
    }
}

(我已在AppendHeader回复中复制了Chtiwi Malek's此处,但这些是在我的web.config处理

我们还必须在OPTIONS

中为我们的申请启用web.config次请求
<handlers>
  <remove name="ExtensionlessUrlHandler-ISAPI-4.0_32bit" />
  <remove name="ExtensionlessUrlHandler-ISAPI-4.0_64bit" />
  <remove name="ExtensionlessUrlHandler-Integrated-4.0" />
  <add name="ExtensionlessUrlHandler-ISAPI-4.0_32bit" path="*." verb="GET,HEAD,POST,DEBUG,PUT,DELETE,PATCH,OPTIONS" modules="IsapiModule" scriptProcessor="%windir%\Microsoft.NET\Framework\v4.0.30319\aspnet_isapi.dll" preCondition="classicMode,runtimeVersionv4.0,bitness32" responseBufferLimit="0" />
  <add name="ExtensionlessUrlHandler-ISAPI-4.0_64bit" path="*." verb="GET,HEAD,POST,DEBUG,PUT,DELETE,PATCH,OPTIONS" modules="IsapiModule" scriptProcessor="%windir%\Microsoft.NET\Framework64\v4.0.30319\aspnet_isapi.dll" preCondition="classicMode,runtimeVersionv4.0,bitness64" responseBufferLimit="0" />
  <add name="ExtensionlessUrlHandler-Integrated-4.0" path="*." verb="GET,HEAD,POST,DEBUG,PUT,DELETE,PATCH,OPTIONS" type="System.Web.Handlers.TransferRequestHandler" preCondition="integratedMode,runtimeVersionv4.0" />
</handlers>