ASP.NET Core MVC中的跨域Cors詳解

在一些項目中,我們經常會用到跨域,在jquery ajax時代,還採用的jsonp的方式進行跨域請求。但這種方式的安全性很低,api端對所有請求均開放了。

如今,NET CORE發展也是越來越完善了,對於跨域,也有完善的策略。

今天我們就來詳細講講 跨域 到底是怎麼一回事兒。

以下示例均以 JQuery  的ajax 爲例。其他xhr 方式的異步請求,雷同。

瀏覽器安全性可防止網頁向不處理網頁的域發送請求。 此限制稱爲同域策略。 同域策略可防止惡意站點從另一站點讀取敏感數據。

瀏覽器默認採用的是同域策略。但有時,我們可能希望允許其他網站向自己的應用發出跨源請求。

我們瀏覽器請求的方法有:PUT,POST,GET,DELETE,OPTIONS,HEAD,PATCH 這幾個。

實際上我們跨域請求某個API 時,瀏覽器是發起了2個請求,一個是OPTIONS 方法,一個是實際的數據請求方法(如GET/POST/PUT等)。

OPTIONS 請求,就像是探子一樣,先去問一下,是否允許跨域請求,若允許,OK,再實際發送請求數據。 如下請求示例的截圖;

 

 

 只有OPTIONS狀態正常,我們的POST請求才能正確獲取到API返回的數據。否則,將報 CORS 跨域異常。

知道了原理之後,我們先看一個簡單的中間件的處理方式。既然是給OPTIONS 請求正確的響應就行,那我們自定義一箇中間件來模擬。

跨域還跟幾個請求頭的配置有關係,Access-Control-Allow-Origin,Access-Control-Allow-Methods,Access-Control-Allow-Headers,Access-Control-Allow-Credentials,即我們可以針對這幾個請求頭進行跨域策略的配置。下面會詳細講解。

我們來看這個中間件怎麼寫

  public class CorsMiddleware
    {
        private readonly RequestDelegate next;

        public CorsMiddleware(RequestDelegate next)
        {
            this.next = next;
        }
        public async Task Invoke(HttpContext context)
        {
            if (context.Request.Headers.ContainsKey(CorsConstants.Origin))
            {
                //定義4個請求頭相關配置
                context.Response.Headers.Add("Access-Control-Allow-Origin", context.Request.Headers["Origin"]); //必須配置,此處寫法作用等於*,可以將context.Request.Headers["Origin"]直接用"*"代替,表示允許所有的請求。或者可配置只允許的域名,多個用英文逗號隔開 如 https://www.1633.com,http:1633.com
                //context.Response.Headers.Add("Access-Control-Allow-Methods", "PUT,POST,GET,DELETE,OPTIONS,HEAD,PATCH"); //可以不配置
                //context.Response.Headers.Add("Access-Control-Allow-Headers", context.Request.Headers["Access-Control-Request-Headers"]); //可以不配置
                //context.Response.Headers.Add("Access-Control-Allow-Credentials", "true"); //可以不配置

                if (context.Request.Method.Equals("OPTIONS"))
                {
                    context.Response.StatusCode = StatusCodes.Status200OK;
                    return;
                }
            }

            await next(context);
        }
    }

然後使用Startup.cs  Configure 方法中調用中間件

  app.UseMiddleware<CorsMiddleware>();

這個中間件,主要就2個配置

1:配置允許的來源頭,Access-Control-Allow-Origin ,若配置爲* ,表示 允許任意來源的請求。

2:當請求方法是OPTIONS 時,直接進行 200 正常響應。

驗證方式也簡單,上面我們提到了以往我們ajax請求跨域,需要用jsonp,這次我們服務端已經加了允許跨域了,就直接用json請求發起。

服務端接口,我們在 Home控制器下定義了Test3 方法,返回一個常規的json格式的內容。

        public JsonResult Test3()
        {
            var data = new { code = 3 };
            return Json(data);
        }

把項目運行起來。然後我們在自己本機桌面上,創建一個 test.html文檔,引入jquery

<!DOCTYPE html>
<html>
<head>
    <script src="https://cdn.bootcdn.net/ajax/libs/jquery/3.6.1/jquery.min.js"></script>
</head>
  
    <body>
        <input id="test3" type="button" value="測試3"/>

    </body>
<script>


    $("#test3").bind("click",function(){
           $.ajax({
            url:"http://localhost:52842/home/test3",
            type:"get",
            dataType:"json",
            error:function(){alert("請求失敗");},
            success:function(json){
                        alert(json.code);
            }
           })

    });
</script>

</html>

直接打開這個html文檔,點擊按鈕

 

 

 說明,我們跨域請求成功了。

假如我們沒調用這個跨域中間件,則會出現下圖結果:

 

 

OK,上面是以自定義中間件的方式來開篇的,下面纔是我們重點要介紹的內容。。。

如何使用NETCore自帶的Cors中間件來設置跨域策略??? 

以下示例是基於.NET6.0的環境,低版本在startup.cs中寫同樣的代碼即可。

主要是調用服務的AddCors進行跨域的配置

 services.AddCors(options =>
{
    options.AddPolicy("MyPolicy", p => p.AllowAnyOrigin().AllowAnyHeader().AllowAnyMethod()); //MyPolicy 爲自定義的策略名稱,與使用時相同即可。可以同時定義多個不同策略名稱的跨域策略
});

然後調用中間件執行

app.UseCors("MyPolicy"); //指定調用MyPolicy 配置的策略,表示全局都採用該策略

這樣,我們就實現了一個很簡單的,全局的跨域策略。

項目運行起來,可以正常跨域。

但,如果你覺得這樣就可以直接用到項目裏面去,那安全性就太低了。

上面的示例中,我們允許了所有的來源。這明顯是不安全的。而且知道的跨域配置項,還有不少。

下面,我們就詳細講下各個配置項,也可參考微軟官方文檔:https://learn.microsoft.com/zh-cn/aspnet/core/security/cors?source=recommendations&view=aspnetcore-7.0

AllowAnyOrigin:允許具有任何方案(http 或 https)的所有源的 CORS 請求。 AllowAnyOrigin 不安全,因爲 任何網站 都可以嚮應用發出跨域請求。

AllowAnyMethod:允許所有的請求方法。

AllowAnyHeader:允許所有請求頭。若不配置這個,當增加我們自定義的請求頭時,跨域就會失敗。

 AllowCredentials:允許有憑據,默認是無,匿名都可以。它不能跟AllowAnyOrigin共存。若是要允許所有的來源,還配置該項,那需要把 .AllowAnyOrigin() 替換成.SetIsOriginAllowed(_=>true)

 

以上4個均是允許所有的請求,不做任何限制。在實際項目中,AllowAnyOrigin,我們是不建議允許所有來源。

那實際項目中,我們應該如何配置,才能既實現跨域,又具有一定的安全性呢?我們採用以下幾個屬性來分別替代

WithOrigins:可替代AllowAnyOrigin方法,可以設定只允許跨域請求的來源域名,域名包含 http部分,最後不能以 /結尾,允許配置多個。

這樣,非我們配置的域名,就不能跨域來訪問我們的資源。

WithMethods:配置允許請求的方法。比如 .WithMethods("POST","GET"),表示只允許POST和GET的請求,其他請求不允許。

WithHeaders:表示請求的Access-Control-Allow-Headers頭必須是與WithHeaders配置的相等,必須是相等纔行,包含也不行。一般情況下,我們可以不配置這個。而是採用AllowAnyHeader。

這樣,配置如下

 services.AddCors(options =>
{
    options.AddPolicy("MyPolicy", p => p.WithOrigins("null", "http://localhost:5151", "https://www.baidu.com")
    .WithMethods("POST","GET")
    .WithHeaders("kywtoken")
    );
});

若實際項目中僅有 來源需要配置,那可以這樣

 services.AddCors(options =>
{
    options.AddPolicy("MyPolicy", p => p.WithOrigins("null", "http://localhost:5151", "https://www.baidu.com")
   
    );
});

若我們的場景是允許同一個主域下的所有子域名都可以跨域請求,則配置的來源可以設置爲通配符。同時 增加 SetIsOriginAllowedToAllowWildcardSubdomains()方法,如下:

 services.AddCors(options =>
{
    options.AddPolicy("MyPolicy", p => p.WithOrigins("https://*.1633.com").SetIsOriginAllowedToAllowWildcardSubdomains()
   
    );
});

這樣,整個.1633.com的子域名都生效。

還有一個方法,SetPreflightMaxAge

這個方法的意思是可以設置OPTIONS的緩存時間,啥意思呢?
上面我們講到了,每次發起一個跨域請求時,其實瀏覽器都會轉成2個請求。 1個是OPTIONS 請求,一個是實際的請求。那對服務器來說,就是2次請求了。

爲了降低服務器的負載,我們可以通過 配置SetPreflightMaxAge 來告知瀏覽器,OPTIONS請求多長時間有效,不用每次都發個OPTIONS來諮詢了。直接發請求過來就可以給你響應了。

如下:

 services.AddCors(options =>
{
    options.AddPolicy("MyPolicy", p => p.WithOrigins("https://*.1633.com").SetIsOriginAllowedToAllowWildcardSubdomains().SetPreflightMaxAge(TimeSpan.FromHours(1))

    ); 
});

我們配置了緩存1小時,也就是1小時內,同個域名的請求,不會重新發起OPTIONS 請求的。就減少了請求的次數。

 

所以綜上,我們一般可以這麼配置:

 services.AddCors(options =>
{
    options.AddPolicy("MyPolicy", p => p.WithOrigins("https://*.1633.com").SetPreflightMaxAge(TimeSpan.FromHours(1)).AllowAnyHeader().AllowAnyMethod().AllowCredentials()
    ); 
});

主要就是配置來源了,其他參數根據項目需要改配置即可。

以上,是全局生效的做法,我們也可以選擇部分生效,包括禁用Cors,選擇哪些路由或者控制器、aciont生效。

具體參考微軟官方文檔:https://learn.microsoft.com/zh-cn/aspnet/core/security/cors?source=recommendations&view=aspnetcore-7.0

 

引申點:

有了這個跨域請求,我們就可以不需要使用JSONP來處理了,可直接像JSON一樣即可。

以前JSONP還只能GET請求,對於增刪改操作不合適。

現在有了跨域,也可以採用POST發起ajax請求了。

==========================================

更多分享,請大家關注我的個人公衆號:

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章