2016-04-26 17 views
8

ASP.NET Core Webアプリケーションで正しくCorsをセットアップしました。イム...ASP.NET Core(Asp.net 5、MVC6、VNext)を使用してすべてのサブドメインを許可するように構成する

"Microsoft.AspNet.Cors": "6.0.0-rc1-final" 

、ここではstartup.csがスニペットです...

public virtual IServiceProvider ConfigureServices(IServiceCollection services) 
{ 
    services.AddCors 
    (
     options => 
     { 
      options.AddPolicy 
      (
       CORSDefaults.PolicyName, 
       builder => 
       { 
        //From config... 
        var allowedDomains = new []{"http://aaa.somewhere.com","https://aaa.somewhere.com","http://bbb.somewhere.com","https://bbb.somewhere.com"}; 

        //Load it 
        builder 
         .WithOrigins(allowedDomains) 
         .AllowAnyHeader() 
         .AllowAnyMethod() 
         .AllowCredentials(); 
       } 
      ); 
     } 
    ); 
} 

を次のパッケージを使用して、これができるようにするサブドメインのリストが急成長していることを除いて素晴らしい作品と私​​が欲しいです「somewhere.com」のすべてのサブドメインを許可します。 "* .somewhere.com"のようなもの。私は新しいASP.NETコア(MVC6、ASP.NET5、VNext)でこれを行う方法に関するドキュメントを見つけることができないようです。私がこれを行う方法を示しているすべてのドキュメント/例は、以前のバージョンのMVCまたはWebApi用です。新しいスタックでこれをどのように達成できますか?

答えて

9

がうまくいけば、それはnugetパッケージにそれを行います。それまでは、この回避策を使用しています。

di ContainerにWildCardCorsServiceクラスを登録しなければならない場合を除いて、通常通りにcorsを登録します。

public virtual IServiceProvider ConfigureServices(IServiceCollection services) 
{ 
    services.TryAdd(ServiceDescriptor.Transient<ICorsService, WildCardCorsService>()); 
    services.AddCors 
    (
     options => 
     { 
      options.AddPolicy 
      (
       CORSDefaults.PolicyName, 
       builder => 
       { 
        builder 
         .WithOrigins("http://*.withwildcardsubdomain.com", "http://nowildcard.com") 
         .AllowAnyHeader() 
         .AllowAnyMethod() 
         .AllowCredentials(); 
       } 
      ); 
     } 
    ); 
} 

このクラスをローカルにソリューションに保存します。ワイルドカードサブドメインを処理できるようにするには、Microsoft.AspNet.Cors.CorsService.csクラスをコピーして編集します。ワイルドカード文字 '*'が見つかると、ルートドメインが許可された起点と実際の起点で一致するかどうかをチェックします。部分ワイルドカードマッチングはサポートされていません。

namespace Microsoft.AspNet.Cors.Infrastructure 
{ 
    /// <summary> 
    /// This ICorsService should be used in place of the official default CorsService to support origins 
    /// like http://*.example.comwhich will allow any subdomain for example.com 
    /// </summary> 
    public class WildCardCorsService : ICorsService 
    { 
     private readonly CorsOptions _options; 

     /// <summary> 
     /// Creates a new instance of the <see cref="CorsService"/>. 
     /// </summary> 
     /// <param name="options">The option model representing <see cref="CorsOptions"/>.</param> 
     public WildCardCorsService(IOptions<CorsOptions> options) 
     { 
      if (options == null) 
      { 
       throw new ArgumentNullException(nameof(options)); 
      } 

      _options = options.Value; 
     } 

     /// <summary> 
     /// Looks up a policy using the <paramref name="policyName"/> and then evaluates the policy using the passed in 
     /// <paramref name="context"/>. 
     /// </summary> 
     /// <param name="requestContext"></param> 
     /// <param name="policyName"></param> 
     /// <returns>A <see cref="CorsResult"/> which contains the result of policy evaluation and can be 
     /// used by the caller to set appropriate response headers.</returns> 
     public CorsResult EvaluatePolicy(HttpContext context, string policyName) 
     { 
      if (context == null) 
      { 
       throw new ArgumentNullException(nameof(context)); 
      } 

      var policy = _options.GetPolicy(policyName); 
      return EvaluatePolicy(context, policy); 
     } 

     /// <inheritdoc /> 
     public CorsResult EvaluatePolicy(HttpContext context, CorsPolicy policy) 
     { 
      if (context == null) 
      { 
       throw new ArgumentNullException(nameof(context)); 
      } 

      if (policy == null) 
      { 
       throw new ArgumentNullException(nameof(policy)); 
      } 

      var corsResult = new CorsResult(); 
      var accessControlRequestMethod = context.Request.Headers[Microsoft.AspNet.Cors.Infrastructure.CorsConstants.AccessControlRequestMethod]; 
      if (string.Equals(context.Request.Method, Microsoft.AspNet.Cors.Infrastructure.CorsConstants.PreflightHttpMethod, StringComparison.Ordinal) && 
       !StringValues.IsNullOrEmpty(accessControlRequestMethod)) 
      { 
       EvaluatePreflightRequest(context, policy, corsResult); 
      } 
      else 
      { 
       EvaluateRequest(context, policy, corsResult); 
      } 

      return corsResult; 
     } 

     public virtual void EvaluateRequest(HttpContext context, CorsPolicy policy, CorsResult result) 
     { 
      var origin = context.Request.Headers[Microsoft.AspNet.Cors.Infrastructure.CorsConstants.Origin]; 
      if (!OriginIsAllowed(origin, policy)) 
      { 
       return; 
      } 

      AddOriginToResult(origin, policy, result); 
      result.SupportsCredentials = policy.SupportsCredentials; 
      AddHeaderValues(result.AllowedExposedHeaders, policy.ExposedHeaders); 
     } 

     public virtual void EvaluatePreflightRequest(HttpContext context, CorsPolicy policy, CorsResult result) 
     { 
      var origin = context.Request.Headers[Microsoft.AspNet.Cors.Infrastructure.CorsConstants.Origin]; 
      if (!OriginIsAllowed(origin, policy)) 
      { 
       return; 
      } 

      var accessControlRequestMethod = context.Request.Headers[Microsoft.AspNet.Cors.Infrastructure.CorsConstants.AccessControlRequestMethod]; 
      if (StringValues.IsNullOrEmpty(accessControlRequestMethod)) 
      { 
       return; 
      } 

      var requestHeaders = 
       context.Request.Headers.GetCommaSeparatedValues(Microsoft.AspNet.Cors.Infrastructure.CorsConstants.AccessControlRequestHeaders); 

      if (!policy.AllowAnyMethod && !policy.Methods.Contains(accessControlRequestMethod)) 
      { 
       return; 
      } 

      if (!policy.AllowAnyHeader && 
       requestHeaders != null && 
       !requestHeaders.All(header => Microsoft.AspNet.Cors.Infrastructure.CorsConstants.SimpleRequestHeaders.Contains(header, StringComparer.OrdinalIgnoreCase) || 
               policy.Headers.Contains(header, StringComparer.OrdinalIgnoreCase))) 
      { 
       return; 
      } 

      AddOriginToResult(origin, policy, result); 
      result.SupportsCredentials = policy.SupportsCredentials; 
      result.PreflightMaxAge = policy.PreflightMaxAge; 
      result.AllowedMethods.Add(accessControlRequestMethod); 
      AddHeaderValues(result.AllowedHeaders, requestHeaders); 
     } 

     /// <inheritdoc /> 
     public virtual void ApplyResult(CorsResult result, HttpResponse response) 
     { 
      if (result == null) 
      { 
       throw new ArgumentNullException(nameof(result)); 
      } 

      if (response == null) 
      { 
       throw new ArgumentNullException(nameof(response)); 
      } 

      var headers = response.Headers; 

      if (result.AllowedOrigin != null) 
      { 
       headers[Microsoft.AspNet.Cors.Infrastructure.CorsConstants.AccessControlAllowOrigin] = result.AllowedOrigin; 
      } 

      if (result.VaryByOrigin) 
      { 
       headers["Vary"] = "Origin"; 
      } 

      if (result.SupportsCredentials) 
      { 
       headers[Microsoft.AspNet.Cors.Infrastructure.CorsConstants.AccessControlAllowCredentials] = "true"; 
      } 

      if (result.AllowedMethods.Count > 0) 
      { 
       // Filter out simple methods 
       var nonSimpleAllowMethods = result.AllowedMethods 
        .Where(m => 
         !Microsoft.AspNet.Cors.Infrastructure.CorsConstants.SimpleMethods.Contains(m, StringComparer.OrdinalIgnoreCase)) 
        .ToArray(); 

       if (nonSimpleAllowMethods.Length > 0) 
       { 
        headers.SetCommaSeparatedValues(
         Microsoft.AspNet.Cors.Infrastructure.CorsConstants.AccessControlAllowMethods, 
         nonSimpleAllowMethods); 
       } 
      } 

      if (result.AllowedHeaders.Count > 0) 
      { 
       // Filter out simple request headers 
       var nonSimpleAllowRequestHeaders = result.AllowedHeaders 
        .Where(header => 
         !Microsoft.AspNet.Cors.Infrastructure.CorsConstants.SimpleRequestHeaders.Contains(header, StringComparer.OrdinalIgnoreCase)) 
        .ToArray(); 

       if (nonSimpleAllowRequestHeaders.Length > 0) 
       { 
        headers.SetCommaSeparatedValues(
         Microsoft.AspNet.Cors.Infrastructure.CorsConstants.AccessControlAllowHeaders, 
         nonSimpleAllowRequestHeaders); 
       } 
      } 

      if (result.AllowedExposedHeaders.Count > 0) 
      { 
       // Filter out simple response headers 
       var nonSimpleAllowResponseHeaders = result.AllowedExposedHeaders 
        .Where(header => 
         !Microsoft.AspNet.Cors.Infrastructure.CorsConstants.SimpleResponseHeaders.Contains(header, StringComparer.OrdinalIgnoreCase)) 
        .ToArray(); 

       if (nonSimpleAllowResponseHeaders.Length > 0) 
       { 
        headers.SetCommaSeparatedValues(
         Microsoft.AspNet.Cors.Infrastructure.CorsConstants.AccessControlExposeHeaders, 
         nonSimpleAllowResponseHeaders); 
       } 
      } 

      if (result.PreflightMaxAge.HasValue) 
      { 
       headers[Microsoft.AspNet.Cors.Infrastructure.CorsConstants.AccessControlMaxAge] 
        = result.PreflightMaxAge.Value.TotalSeconds.ToString(CultureInfo.InvariantCulture); 
      } 
     } 

     protected virtual bool OriginIsAllowed(string origin, CorsPolicy policy) 
     { 
      if (!string.IsNullOrWhiteSpace(origin) && 
       (policy.AllowAnyOrigin || 
       policy.Origins.Contains(origin) || 
       IsWildCardSubdomainMatch(origin, policy))) 
       return true; 

      return false; 
     } 

     private void AddOriginToResult(string origin, CorsPolicy policy, CorsResult result) 
     { 
      if (policy.AllowAnyOrigin) 
      { 
       if (policy.SupportsCredentials) 
       { 
        result.AllowedOrigin = origin; 
        result.VaryByOrigin = true; 
       } 
       else 
       { 
        result.AllowedOrigin = Microsoft.AspNet.Cors.Infrastructure.CorsConstants.AnyOrigin; 
       } 
      } 
      else 
      { 
       result.AllowedOrigin = origin; 
      } 
     } 

     private static void AddHeaderValues(IList<string> target, IEnumerable<string> headerValues) 
     { 
      if (headerValues == null) 
      { 
       return; 
      } 

      foreach (var current in headerValues) 
      { 
       target.Add(current); 
      } 
     } 

     private bool IsWildCardSubdomainMatch(string origin, CorsPolicy policy) 
     { 
      var actualOriginUri = new Uri(origin); 
      var actualOriginRootDomain = GetRootDomain(actualOriginUri); 

      foreach (var o in policy.Origins) 
      { 
       if (!o.Contains("*")) 
        continue; 

       // 1) CANNOT USE System.Text.RegularExpression since it does not exist in .net platform 5.4 (which the Microsoft.AspNet.Cors project.json targets) 
       // 2) '*' char is not valid for creation of a URI object so we replace it just for this comparison 
       var allowedOriginUri = new Uri(o.Replace("*", "SOMELETTERS")); 
       if (allowedOriginUri.Scheme == actualOriginUri.Scheme && 
        actualOriginRootDomain == GetRootDomain(allowedOriginUri)) 
        return true; 
      } 

      return false; 
     } 

     private string GetRootDomain(Uri uri) 
     { 
      //Got this snippet here http://stackoverflow.com/questions/16473838/get-domain-name-of-a-url-in-c-sharp-net 
      var host = uri.Host; 
      int index = host.LastIndexOf('.'), last = 3; 

      while (index > 0 && index >= last - 3) 
      { 
       last = index; 
       index = host.LastIndexOf('.', last - 1); 
      } 

      return host.Substring(index + 1); 
     } 
    } 

    /// <summary> 
    /// Needed to copy these in since some of them are internal to the Microsoft.AspNet.Cors project 
    /// </summary> 
    public static class CorsConstants 
    { 
     /// <summary>The HTTP method for the CORS preflight request.</summary> 
     public static readonly string PreflightHttpMethod = "OPTIONS"; 
     /// <summary>The Origin request header.</summary> 
     public static readonly string Origin = "Origin"; 
     /// <summary> 
     /// The value for the Access-Control-Allow-Origin response header to allow all origins. 
     /// </summary> 
     public static readonly string AnyOrigin = "*"; 
     /// <summary>The Access-Control-Request-Method request header.</summary> 
     public static readonly string AccessControlRequestMethod = "Access-Control-Request-Method"; 
     /// <summary>The Access-Control-Request-Headers request header.</summary> 
     public static readonly string AccessControlRequestHeaders = "Access-Control-Request-Headers"; 
     /// <summary>The Access-Control-Allow-Origin response header.</summary> 
     public static readonly string AccessControlAllowOrigin = "Access-Control-Allow-Origin"; 
     /// <summary>The Access-Control-Allow-Headers response header.</summary> 
     public static readonly string AccessControlAllowHeaders = "Access-Control-Allow-Headers"; 
     /// <summary>The Access-Control-Expose-Headers response header.</summary> 
     public static readonly string AccessControlExposeHeaders = "Access-Control-Expose-Headers"; 
     /// <summary>The Access-Control-Allow-Methods response header.</summary> 
     public static readonly string AccessControlAllowMethods = "Access-Control-Allow-Methods"; 
     /// <summary>The Access-Control-Allow-Credentials response header.</summary> 
     public static readonly string AccessControlAllowCredentials = "Access-Control-Allow-Credentials"; 
     /// <summary>The Access-Control-Max-Age response header.</summary> 
     public static readonly string AccessControlMaxAge = "Access-Control-Max-Age"; 
     internal static readonly string[] SimpleRequestHeaders = new string[4] 
     { 
     "Origin", 
     "Accept", 
     "Accept-Language", 
     "Content-Language" 
     }; 
     internal static readonly string[] SimpleResponseHeaders = new string[6] 
     { 
     "Cache-Control", 
     "Content-Language", 
     "Content-Type", 
     "Expires", 
     "Last-Modified", 
     "Pragma" 
     }; 
     internal static readonly string[] SimpleMethods = new string[3] 
     { 
     "GET", 
     "HEAD", 
     "POST" 
     }; 
    } 
} 

お楽しみください!

+0

リクエストごとに新しいインスタンスを作成するトランジェントを登録するのではなく、これをシングルトンとして登録する必要がありますか? – michaelmsm89

+0

どこに追加すればよいですか? – Si8

+0

これはかなり滑らかです – Nexxas

1

アウトオブザボックスCorsServiceは、policy.Origins.Contains(origin)を使用してリクエストを評価します。したがって、Listには原点が含まれている必要があるため、必要なことを行うための些細な方法がないように見えます。独自のICorsServiceを実装し、すぐに使用できるボックスCorsServiceを継承し、*.mydomain.comワイルドカードを処理する方法を調整することができます。

編集yo aspnetを使用して達成したものは、1.0.0-rc1-update2 Web APIプロジェクトを生成するものです。できます。 Startup.csにあなたのサービスを登録します(詳細についてはCorsServiceCollectionExtensionsを参照してください。)

public class Startup 
{ 
    public void ConfigureServices(IServiceCollection services) 
    { 
     services.AddOptions(); 

     services.TryAdd(
      ServiceDescriptor.Transient<ICorsService, MyCorsService>()); 

     services.TryAdd(
      ServiceDescriptor.Transient<ICorsPolicyProvider, DefaultCorsPolicyProvider>()); 
    } 

    public void Configure(IApplicationBuilder app, ILoggerFactory loggerFactory) 
    { 
     loggerFactory.AddConsole(minLevel: LogLevel.Verbose); 

     app.UseCors(corsPolictyBuilder => 
     { 
      corsPolictyBuilder.WithOrigins("*.mydomain.com"); 
     }); 

     app.Run(async context => 
     { 
      await context.Response.WriteAsync(
       $"Is Cors? {context.Request.Headers.ContainsKey(CorsConstants.Origin)}"); 
     }); 
    } 
} 

はここにあなたの実装を待って、サービスです。 CorsServiceからコピー/貼り付けまたは継承することができます。この変更とASP.NETチームにI submitted a pull request

public class MyCorsService : CorsService, ICorsService 
{ 
    private ILogger _logger; 

    public MyCorsService(IOptions<CorsOptions> options, ILogger<MyCorsService> logger) 
     : base(options) 
    { 
     _logger = logger; 
     _logger.LogInformation("MyCorsService"); 
    } 

    public override void ApplyResult(
     CorsResult result, HttpResponse response) 
    { 
     _logger.LogInformation("ApplyResult"); 
     base.ApplyResult(result, response); 
    } 

    public override void EvaluateRequest(
     HttpContext context, CorsPolicy policy, CorsResult result) 
    { 
     _logger.LogInformation("EvaluateRequest"); 
     base.EvaluateRequest(context, policy, result); 
    } 

    public override void EvaluatePreflightRequest(
     HttpContext context, CorsPolicy policy, CorsResult result) 
    { 
     _logger.LogInformation("EvaluatePreflightRequest"); 
     base.EvaluatePreflightRequest(context, policy, result); 
    } 
} 
+1

私は、あまり手をつけていないものがあることを望んでいました。上記のアプローチをとった場合、現在の実装ではなく、その実装を注入/使用する最善の方法は何でしょうか?それでもAsp.net Coreが提供するすべての可能なDIアプローチの周りに私の頭を包み込みます。ありがとう! – sjdirect

+0

@sjdirect RC2を使用しますか? –

+1

Shaun、この時点で私はアップグレードに興奮しないだろうが、私はこの問題に関してRC2がもたらす救済について聞きたい。 – sjdirect

関連する問題