在 ASP.NET Core 中啟用 CORS (跨原始來源要求)
作者:Rick Anderson 與 Kirk Larkin
本文說明如何在 ASP.NET Core應用程式中啟用 CORS。
瀏覽器安全性可防止網頁向提供網頁的不同網域提出要求。 此限制稱為 相同原始來源原則。 相同的來源原則可防止惡意網站從另一個網站讀取敏感性資料。 有時候,您可能想要允許其他網站對您的應用程式提出跨原始來源要求。 如需詳細資訊,請參閱 Mozilla CORS 一文。
跨原始來源資源分享 (CORS) :
- 這是 W3C 標準,可讓伺服器放寬相同的原始來源原則。
- 不是安全性功能,CORS 會放寬安全性。 藉由允許 CORS,API 並不更安全。 如需詳細資訊,請參閱 CORS 的運作方式。
- 允許伺服器明確允許某些跨原始來源要求,同時拒絕其他要求。
- 比舊版技術更安全且更有彈性,例如JS ONP。
檢視或下載範例程式碼 \(英文\) (如何下載)
相同原點
如果兩個 URL 具有相同的配置、主機和埠 (RFC 6454) ,則兩個 URL 具有相同的來源。
這兩個 URL 具有相同的來源:
https://example.com/foo.html
https://example.com/bar.html
這些 URL 的來源與前兩個 URL 不同:
https://example.net
:不同的網域https://www.example.com/foo.html
:不同的子域http://example.com/foo.html
:不同的配置https://example.com:9000/foo.html
:不同的埠
啟用 CORS
有三種方式可以啟用 CORS:
- 在中介軟體中使用 具名原則 或 預設原則。
- 使用 端點路由。
- 使用 [EnableCors] 屬性。
搭配具名原則使用 [EnableCors] 屬性可提供最精細的控制,以限制支援 CORS 的端點。
警告
UseCors 必須以正確的順序呼叫。 如需詳細資訊,請參閱 中介軟體順序。 例如, UseCors
在使用 之前 UseResponseCachingUseResponseCaching
,必須先呼叫 。
下列各節會詳細說明每個方法。
具有具名原則和中介軟體的 CORS
CORS 中介軟體會處理跨原始來源的要求。 下列程式碼會將 CORS 原則套用至具有指定來源的所有應用程式端點:
var MyAllowSpecificOrigins = "_myAllowSpecificOrigins";
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddCors(options =>
{
options.AddPolicy(name: MyAllowSpecificOrigins,
policy =>
{
policy.WithOrigins("http://example.com",
"http://www.contoso.com");
});
});
// services.AddResponseCaching();
builder.Services.AddControllers();
var app = builder.Build();
app.UseHttpsRedirection();
app.UseStaticFiles();
app.UseRouting();
app.UseCors(MyAllowSpecificOrigins);
app.UseAuthorization();
app.MapControllers();
app.Run();
上述程式碼:
- 將原則名稱設定為
_myAllowSpecificOrigins
。 原則名稱是任意的。 - UseCors呼叫擴充方法並指定
_myAllowSpecificOrigins
CORS 原則。UseCors
會新增 CORS 中介軟體。 的呼叫UseCors
必須放在 之後UseRouting
,但在 之前UseAuthorization
。 如需詳細資訊,請參閱 中介軟體順序。 - 使用Lambda 運算式呼叫 AddCors 。 Lambda 會接受 CorsPolicyBuilder 物件。 本文稍後會說明組態選項,例如
WithOrigins
。 _myAllowSpecificOrigins
啟用所有控制器端點的 CORS 原則。 請參閱 端點路由 以將 CORS 原則套用至特定端點。- 使用回應快取中介軟體時,請在 之前 UseResponseCaching 呼叫 UseCors 。
使用端點路由時,CORS中介軟體必須設定為在 對 和 UseEndpoints
的呼叫 UseRouting
之間執行。
如需類似上述程式碼的測試程式碼指示,請參閱 測試 CORS 。
方法呼叫會將 AddCors CORS 服務新增至應用程式的服務容器:
var MyAllowSpecificOrigins = "_myAllowSpecificOrigins";
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddCors(options =>
{
options.AddPolicy(name: MyAllowSpecificOrigins,
policy =>
{
policy.WithOrigins("http://example.com",
"http://www.contoso.com");
});
});
// services.AddResponseCaching();
builder.Services.AddControllers();
var app = builder.Build();
app.UseHttpsRedirection();
app.UseStaticFiles();
app.UseRouting();
app.UseCors(MyAllowSpecificOrigins);
app.UseAuthorization();
app.MapControllers();
app.Run();
如需詳細資訊,請參閱本檔中 的 CORS 原則選項 。
方法 CorsPolicyBuilder 可以鏈結,如下列程式碼所示:
var MyAllowSpecificOrigins = "_myAllowSpecificOrigins";
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddCors(options =>
{
options.AddPolicy(MyAllowSpecificOrigins,
policy =>
{
policy.WithOrigins("http://example.com",
"http://www.contoso.com")
.AllowAnyHeader()
.AllowAnyMethod();
});
});
builder.Services.AddControllers();
var app = builder.Build();
app.UseHttpsRedirection();
app.UseStaticFiles();
app.UseRouting();
app.UseCors(MyAllowSpecificOrigins);
app.UseAuthorization();
app.MapControllers();
app.Run();
注意:指定的 URL 不得 包含結尾斜線 (/
) 。 如果 URL 以 終止 /
,則比較會 false
傳回 ,而且不會傳回任何標頭。
UseCors 和 UseStaticFiles 順序
一般而言, UseStaticFiles
會在 之前 UseCors
呼叫 。 使用 JavaScript 跨網站擷取靜態檔案的應用程式必須在 之前 UseStaticFiles
呼叫 UseCors
。
具有預設原則和中介軟體的 CORS
下列醒目提示的程式碼會啟用預設 CORS 原則:
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddCors(options =>
{
options.AddDefaultPolicy(
policy =>
{
policy.WithOrigins("http://example.com",
"http://www.contoso.com");
});
});
builder.Services.AddControllers();
var app = builder.Build();
app.UseHttpsRedirection();
app.UseStaticFiles();
app.UseRouting();
app.UseCors();
app.UseAuthorization();
app.MapControllers();
app.Run();
上述程式碼會將預設 CORS 原則套用至所有控制器端點。
啟用具有端點路由的 Cors
使用端點路由時,可以使用擴充方法集,以 RequireCors 個別端點為基礎啟用 CORS:
var MyAllowSpecificOrigins = "_myAllowSpecificOrigins";
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddCors(options =>
{
options.AddPolicy(name: MyAllowSpecificOrigins,
policy =>
{
policy.WithOrigins("http://example.com",
"http://www.contoso.com");
});
});
builder.Services.AddControllers();
builder.Services.AddRazorPages();
var app = builder.Build();
app.UseHttpsRedirection();
app.UseStaticFiles();
app.UseRouting();
app.UseCors();
app.UseAuthorization();
app.UseEndpoints(endpoints =>
{
endpoints.MapGet("/echo",
context => context.Response.WriteAsync("echo"))
.RequireCors(MyAllowSpecificOrigins);
endpoints.MapControllers()
.RequireCors(MyAllowSpecificOrigins);
endpoints.MapGet("/echo2",
context => context.Response.WriteAsync("echo2"));
endpoints.MapRazorPages();
});
app.Run();
在上述程式碼中:
app.UseCors
會啟用 CORS 中介軟體。 因為尚未設定預設原則,app.UseCors()
所以單獨不會啟用 CORS。/echo
和 控制器端點允許使用指定原則的跨原始來源要求。- 和 Pages 端點不允許跨原始來源要求,因為未指定預設原則。 Razor
/echo2
[DisableCors]屬性不會停用端點路由 RequireCors
所啟用的 CORS。
如需測試類似上述程式碼的指示,請參閱 Test CORS with [EnableCors] 屬性和 RequireCors 方法 。
使用屬性啟用 CORS
使用 [EnableCors] 屬性啟用 CORS,並將具名原則套用至只有需要 CORS 的端點可提供最精細的控制。
[EnableCors]屬性提供全域套用 CORS 的替代方案。 屬性 [EnableCors]
會啟用所選端點的 CORS,而不是所有端點:
[EnableCors]
指定預設原則。[EnableCors("{Policy String}")]
指定具名原則。
屬性 [EnableCors]
可以套用至:
- Razor 網頁
PageModel
- 控制器
- 控制器動作方法
不同的原則可以套 [EnableCors]
用至具有 屬性的控制器、頁面模型或動作方法。 [EnableCors]
當屬性套用至控制器、頁面模型或動作方法,並在中介軟體中啟用 CORS 時,會套用這兩個原則。 建議您不要合併原則。 使用 [EnableCors]
屬性或中介軟體,而不是在同一個應用程式中。
下列程式碼會將不同的原則套用至每個方法:
[Route("api/[controller]")]
[ApiController]
public class WidgetController : ControllerBase
{
// GET api/values
[EnableCors("AnotherPolicy")]
[HttpGet]
public ActionResult<IEnumerable<string>> Get()
{
return new string[] { "green widget", "red widget" };
}
// GET api/values/5
[EnableCors("Policy1")]
[HttpGet("{id}")]
public ActionResult<string> Get(int id)
{
return id switch
{
1 => "green widget",
2 => "red widget",
_ => NotFound(),
};
}
}
下列程式碼會建立兩個 CORS 原則:
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddCors(options =>
{
options.AddPolicy("Policy1",
policy =>
{
policy.WithOrigins("http://example.com",
"http://www.contoso.com");
});
options.AddPolicy("AnotherPolicy",
policy =>
{
policy.WithOrigins("http://www.contoso.com")
.AllowAnyHeader()
.AllowAnyMethod();
});
});
builder.Services.AddControllers();
var app = builder.Build();
app.UseHttpsRedirection();
app.UseRouting();
app.UseCors();
app.UseAuthorization();
app.MapControllers();
app.Run();
對於限制 CORS 要求的最精細控制:
- 搭配具名原則使用
[EnableCors("MyPolicy")]
。 - 請勿定義預設原則。
- 請勿使用 端點路由。
下一節中的程式碼符合上述清單。
如需類似上述程式碼的測試程式碼指示,請參閱 測試 CORS 。
停用 CORS
[DisableCors]屬性不會停用端點路由已啟用的 CORS。
下列程式碼會定義 CORS 原則 "MyPolicy"
:
var MyAllowSpecificOrigins = "_myAllowSpecificOrigins";
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddCors(options =>
{
options.AddPolicy(name: "MyPolicy",
policy =>
{
policy.WithOrigins("http://example.com",
"http://www.contoso.com")
.WithMethods("PUT", "DELETE", "GET");
});
});
builder.Services.AddControllers();
builder.Services.AddRazorPages();
var app = builder.Build();
app.UseHttpsRedirection();
app.UseStaticFiles();
app.UseRouting();
app.UseCors();
app.UseAuthorization();
app.UseEndpoints(endpoints => {
endpoints.MapControllers();
endpoints.MapRazorPages();
});
app.Run();
下列程式碼會停用動作的 GetValues2
CORS:
[EnableCors("MyPolicy")]
[Route("api/[controller]")]
[ApiController]
public class ValuesController : ControllerBase
{
// GET api/values
[HttpGet]
public IActionResult Get() =>
ControllerContext.MyDisplayRouteInfo();
// GET api/values/5
[HttpGet("{id}")]
public IActionResult Get(int id) =>
ControllerContext.MyDisplayRouteInfo(id);
// PUT api/values/5
[HttpPut("{id}")]
public IActionResult Put(int id) =>
ControllerContext.MyDisplayRouteInfo(id);
// GET: api/values/GetValues2
[DisableCors]
[HttpGet("{action}")]
public IActionResult GetValues2() =>
ControllerContext.MyDisplayRouteInfo();
}
上述程式碼:
- 不會啟用具有 端點路由的 CORS。
- 不會定義 預設 CORS 原則。
- 使用 [EnableCors (「MyPolicy」) ] 來啟用
"MyPolicy"
控制器的 CORS 原則。 - 停用 方法的
GetValues2
CORS。
如需測試上述程式碼的指示,請參閱 測試 CORS 。
CORS 原則選項
本節說明可在 CORS 原則中設定的各種選項:
AddPolicy 在 中 Program.cs
呼叫 。 對於某些選項,先閱讀 CORS 的運作方式 一節可能會很有説明。
設定允許的來源
AllowAnyOrigin:允許來自所有來源的 CORS 要求具有任何配置 (http
或 https
) 。 AllowAnyOrigin
不安全,因為 任何網站 都可以對應用程式提出跨原始來源要求。
注意
指定 AllowAnyOrigin
和 AllowCredentials
是不安全的組態,而且可能會導致跨網站偽造要求。 當應用程式使用這兩種方法設定時,CORS 服務會傳回無效 CORS 回應。
AllowAnyOrigin
會影響預檢要求和 Access-Control-Allow-Origin
標頭。 如需詳細資訊,請參閱 預檢要求 一節。
SetIsOriginAllowedToAllowWildcardSubdomains:將原則 IsOriginAllowed 的 屬性設定為函式,以允許原始來源在評估是否允許來源時比對已設定的萬用字元網域。
var MyAllowSpecificOrigins = "_MyAllowSubdomainPolicy";
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddCors(options =>
{
options.AddPolicy(name: MyAllowSpecificOrigins,
policy =>
{
policy.WithOrigins("https://*.example.com")
.SetIsOriginAllowedToAllowWildcardSubdomains();
});
});
builder.Services.AddControllers();
var app = builder.Build();
設定允許的 HTTP 方法
- 允許任何 HTTP 方法:
- 影響預檢要求和
Access-Control-Allow-Methods
標頭。 如需詳細資訊,請參閱 預檢要求 一節。
設定允許的要求標頭
若要允許在 CORS 要求中傳送特定標頭,稱為 作者要求標頭、呼叫 WithHeaders 並指定允許的標頭:
using Microsoft.Net.Http.Headers;
var MyAllowSpecificOrigins = "_MyAllowSubdomainPolicy";
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddCors(options =>
{
options.AddPolicy(name: MyAllowSpecificOrigins,
policy =>
{
policy.WithOrigins("http://example.com")
.WithHeaders(HeaderNames.ContentType, "x-custom-header");
});
});
builder.Services.AddControllers();
var app = builder.Build();
若要允許所有 作者要求標頭,請呼叫 AllowAnyHeader :
var MyAllowSpecificOrigins = "_MyAllowSubdomainPolicy";
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddCors(options =>
{
options.AddPolicy(name: MyAllowSpecificOrigins,
policy =>
{
policy.WithOrigins("https://*.example.com")
.AllowAnyHeader();
});
});
builder.Services.AddControllers();
var app = builder.Build();
AllowAnyHeader
會影響預檢要求和 Access-Control-Request-Headers 標頭。 如需詳細資訊,請參閱 預檢要求 一節。
只有在 所 Access-Control-Request-Headers
傳送的標頭完全符合 中所述 WithHeaders
的標頭時,才可能符合 所 WithHeaders
指定之特定標頭的 CORS 中介軟體原則。
例如,請考慮設定的應用程式,如下所示:
app.UseCors(policy => policy.WithHeaders(HeaderNames.CacheControl));
CORS 中介軟體會拒絕具有下列要求標頭的預檢要求,因為 Content-Language
(HeaderNames.ContentLanguage) 未列于 : WithHeaders
Access-Control-Request-Headers: Cache-Control, Content-Language
應用程式會傳回 200 OK 回應,但不會將 CORS 標頭傳回。 因此,瀏覽器不會嘗試跨原始來源要求。
設定公開的回應標頭
根據預設,瀏覽器不會將所有回應標頭公開給應用程式。 如需詳細資訊,請參閱 W3C 跨原始來源資源分享 (術語) :簡單回應標頭。
預設可用的回應標頭如下:
Cache-Control
Content-Language
Content-Type
Expires
Last-Modified
Pragma
CORS 規格會呼叫這些標頭 的簡單回應標頭。 若要讓應用程式使用其他標頭,請呼叫 WithExposedHeaders :
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddCors(options =>
{
options.AddPolicy("MyExposeResponseHeadersPolicy",
policy =>
{
policy.WithOrigins("https://*.example.com")
.WithExposedHeaders("x-custom-header");
});
});
builder.Services.AddControllers();
var app = builder.Build();
跨原始來源要求中的認證
認證需要在 CORS 要求中特殊處理。 根據預設,瀏覽器不會傳送具有跨原始來源要求的認證。 認證包括 cookie s 和 HTTP 驗證配置。 若要傳送具有跨原始來源要求的認證,用戶端必須設定 XMLHttpRequest.withCredentials
為 true
。
直接使用 XMLHttpRequest
:
var xhr = new XMLHttpRequest();
xhr.open('get', 'https://www.example.com/api/test');
xhr.withCredentials = true;
使用 jQuery:
$.ajax({
type: 'get',
url: 'https://www.example.com/api/test',
xhrFields: {
withCredentials: true
}
});
使用 擷取 API:
fetch('https://www.example.com/api/test', {
credentials: 'include'
});
伺服器必須允許認證。 若要允許跨原始來源認證,請呼叫 AllowCredentials :
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddCors(options =>
{
options.AddPolicy("MyMyAllowCredentialsPolicy",
policy =>
{
policy.WithOrigins("http://example.com")
.AllowCredentials();
});
});
builder.Services.AddControllers();
var app = builder.Build();
HTTP 回應包含 Access-Control-Allow-Credentials
標頭,告知瀏覽器伺服器允許跨原始來源要求的認證。
如果瀏覽器傳送認證,但回應不包含有效的 Access-Control-Allow-Credentials
標頭,瀏覽器就不會向應用程式公開回應,而且跨原始來源要求會失敗。
允許跨原始來源認證是安全性風險。 另一個網域的網站可以代表使用者將登入使用者的認證傳送給應用程式,而不需要使用者知道。
CORS 規格也指出,如果 Access-Control-Allow-Credentials
標頭存在,將原點設定為 "*"
(所有原點) 無效。
預檢要求
對於某些 CORS 要求,瀏覽器會在提出實際要求之前傳送額外的 OPTIONS 要求。 此要求稱為 預檢要求。 如果 下列所有 條件都成立,瀏覽器可以略過預檢要求:
- 要求方法是 GET、HEAD 或 POST。
- 應用程式不會設定 、、
Accept-Language
Content-Language
、Content-Type
或Last-Event-ID
以外的Accept
要求標頭。 Content-Type
如果已設定標頭,則具有下列其中一個值:application/x-www-form-urlencoded
multipart/form-data
text/plain
用戶端要求所設定之要求標頭的規則會套用至應用程式在 物件上呼叫 setRequestHeader
所設定的 XMLHttpRequest
標頭。 CORS 規格會呼叫這些標頭 作者要求標頭。 此規則不適用於瀏覽器可以設定的標頭,例如 User-Agent
、 Host
或 Content-Length
。
以下是類似本檔 [測試 CORS] 區段中 [Put test]按鈕所提出預檢要求的範例回應。
General:
Request URL: https://cors3.azurewebsites.net/api/values/5
Request Method: OPTIONS
Status Code: 204 No Content
Response Headers:
Access-Control-Allow-Methods: PUT,DELETE,GET
Access-Control-Allow-Origin: https://cors1.azurewebsites.net
Server: Microsoft-IIS/10.0
Set-Cookie: ARRAffinity=8f8...8;Path=/;HttpOnly;Domain=cors1.azurewebsites.net
Vary: Origin
Request Headers:
Accept: */*
Accept-Encoding: gzip, deflate, br
Accept-Language: en-US,en;q=0.9
Access-Control-Request-Method: PUT
Connection: keep-alive
Host: cors3.azurewebsites.net
Origin: https://cors1.azurewebsites.net
Referer: https://cors1.azurewebsites.net/
Sec-Fetch-Dest: empty
Sec-Fetch-Mode: cors
Sec-Fetch-Site: cross-site
User-Agent: Mozilla/5.0
預檢要求會使用 HTTP OPTIONS 方法。 它可能包含下列標頭:
- Access-Control-Request-Method:將用於實際要求的 HTTP 方法。
- Access-Control-Request-Headers:應用程式在實際要求上設定的要求標頭清單。 如先前所述,這不包含瀏覽器設定的標頭,例如
User-Agent
。 - Access-Control-Allow-Methods
如果預檢要求遭到拒絕,應用程式會 200 OK
傳迴響應,但未設定 CORS 標頭。 因此,瀏覽器不會嘗試跨原始來源要求。 如需拒絕預檢要求的範例,請參閱本檔的 Test CORS 一節。
使用 F12 工具時,主控台應用程式會顯示類似下列其中一項的錯誤,視瀏覽器而定:
- Firefox:已封鎖跨原始來源要求:相同的原始來源原則不允許讀取位於
https://cors1.azurewebsites.net/api/TodoItems1/MyDelete2/5
的遠端資源。 (原因:CORS 要求未成功) 。 深入了解 - 以Chromium為基礎:CORS 原則已封鎖從來源 ' https://cors1.azurewebsites.net/api/TodoItems1/MyDelete2/5https://cors3.azurewebsites.net ' 擷取存取權:對預檢要求回應不會通過存取控制檢查:要求的資源上沒有 'Access-Control-Allow-Origin' 標頭存在。 如果不透明回應適合您的需求,請將要求的模式設定為 'no-cors' 以在停用 CORS 之下擷取資源。
若要允許特定標頭,請呼叫 WithHeaders :
using Microsoft.Net.Http.Headers;
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddCors(options =>
{
options.AddPolicy("MyAllowHeadersPolicy",
policy =>
{
policy.WithOrigins("http://example.com")
.WithHeaders(HeaderNames.ContentType, "x-custom-header");
});
});
builder.Services.AddControllers();
var app = builder.Build();
若要允許所有 作者要求標頭,請呼叫 AllowAnyHeader :
using Microsoft.Net.Http.Headers;
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddCors(options =>
{
options.AddPolicy("MyAllowAllHeadersPolicy",
policy =>
{
policy.WithOrigins("https://*.example.com")
.AllowAnyHeader();
});
});
builder.Services.AddControllers();
var app = builder.Build();
瀏覽器在設定 Access-Control-Request-Headers
的方式中不一致。 如果下列其中一項:
- 標頭設定為以外的任何專案
"*"
- AllowAnyHeader 稱為:至少
Accept
包含 、Content-Type
和Origin
,以及您想要支援的任何自訂標頭。
自動預檢要求碼
套用 CORS 原則時:
- 在 中
Program.cs
呼叫 ,以全域方式呼叫app.UseCors
。 [EnableCors]
使用 屬性。
ASP.NET Core回應預檢 OPTIONS 要求。
本檔的 [測試 CORS ] 區段示範此行為。
[HttpOptions] 預檢要求的屬性
使用適當的原則啟用 CORS 時,ASP.NET Core通常會自動回應 CORS 預檢要求。
下列程式碼會使用 [HttpOptions] 屬性來建立 OPTIONS 要求的端點:
[Route("api/[controller]")]
[ApiController]
public class TodoItems2Controller : ControllerBase
{
// OPTIONS: api/TodoItems2/5
[HttpOptions("{id}")]
public IActionResult PreflightRoute(int id)
{
return NoContent();
}
// OPTIONS: api/TodoItems2
[HttpOptions]
public IActionResult PreflightRoute()
{
return NoContent();
}
[HttpPut("{id}")]
public IActionResult PutTodoItem(int id)
{
if (id < 1)
{
return BadRequest();
}
return ControllerContext.MyDisplayRouteInfo(id);
}
如需測試上述程式碼的指示,請參閱 使用 [EnableCors] 屬性和 RequireCors 方法來測試 CORS 。
設定預檢到期時間
Access-Control-Max-Age
標頭會指定快取預檢要求回應的時間長度。 若要設定此標頭,請呼叫 SetPreflightMaxAge :
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddCors(options =>
{
options.AddPolicy("MySetPreflightExpirationPolicy",
policy =>
{
policy.WithOrigins("http://example.com")
.SetPreflightMaxAge(TimeSpan.FromSeconds(2520));
});
});
builder.Services.AddControllers();
var app = builder.Build();
CORS 的運作方式
本節說明在 HTTP 訊息層級的 CORS 要求中會發生什麼情況。
- CORS 不是 安全性功能。 CORS 是 W3C 標準,可讓伺服器放寬相同原始原則。
- 例如,惡意執行者可以使用 跨網站腳本 (XSS) ,並對其啟用 CORS 的網站執行跨網站要求來竊取資訊。
- 藉由允許 CORS,API 並不更安全。
- 用戶端 (瀏覽器) 強制執行 CORS。 伺服器會執行要求並傳迴響應,這是傳回錯誤的用戶端,並封鎖回應。 例如,下列任何工具都會顯示伺服器回應:
- Fiddler
- Postman
- .NET HttpClient
- 在網址列中輸入 URL,以網頁瀏覽器。
- 用戶端 (瀏覽器) 強制執行 CORS。 伺服器會執行要求並傳迴響應,這是傳回錯誤的用戶端,並封鎖回應。 例如,下列任何工具都會顯示伺服器回應:
- 伺服器允許瀏覽器執行跨原始來源 XHR 或 擷取 API 要求的方式,否則會遭到禁止。
- 沒有 CORS 的瀏覽器無法執行跨原始來源要求。 在 CORS 之前,JS ONP是用來規避這項限制。 JSONP 不使用 XHR,它會使用
<script>
標記來接收回應。 允許跨原始來源載入腳本。
- 沒有 CORS 的瀏覽器無法執行跨原始來源要求。 在 CORS 之前,JS ONP是用來規避這項限制。 JSONP 不使用 XHR,它會使用
CORS 規格引進了數個新的 HTTP 標頭,可啟用跨原始來源要求。 如果瀏覽器支援 CORS,它會針對跨原始來源要求自動設定這些標頭。 不需要自訂 JavaScript 程式碼才能啟用 CORS。
以下是從 [ 值 ] 測試按鈕到 https://cors1.azurewebsites.net/api/values
的跨原始來源要求範例。 Origin
標頭:
- 提供提出要求之網站的網域。
- 為必要專案,且必須與主機不同。
一般標頭
Request URL: https://cors1.azurewebsites.net/api/values
Request Method: GET
Status Code: 200 OK
回應標頭
Content-Encoding: gzip
Content-Type: text/plain; charset=utf-8
Server: Microsoft-IIS/10.0
Set-Cookie: ARRAffinity=8f...;Path=/;HttpOnly;Domain=cors1.azurewebsites.net
Transfer-Encoding: chunked
Vary: Accept-Encoding
X-Powered-By: ASP.NET
要求標頭
Accept: */*
Accept-Encoding: gzip, deflate, br
Accept-Language: en-US,en;q=0.9
Connection: keep-alive
Host: cors1.azurewebsites.net
Origin: https://cors3.azurewebsites.net
Referer: https://cors3.azurewebsites.net/
Sec-Fetch-Dest: empty
Sec-Fetch-Mode: cors
Sec-Fetch-Site: cross-site
User-Agent: Mozilla/5.0 ...
在 OPTIONS
要求中,伺服器會在回應中設定Response 標頭Access-Control-Allow-Origin: {allowed origin}
。 例如,已部署 的範例Delete [EnableCors] 按鈕 OPTIONS
要求包含下列標頭:
一般標頭
Request URL: https://cors3.azurewebsites.net/api/TodoItems2/MyDelete2/5
Request Method: OPTIONS
Status Code: 204 No Content
回應標頭
Access-Control-Allow-Headers: Content-Type,x-custom-header
Access-Control-Allow-Methods: PUT,DELETE,GET,OPTIONS
Access-Control-Allow-Origin: https://cors1.azurewebsites.net
Server: Microsoft-IIS/10.0
Set-Cookie: ARRAffinity=8f...;Path=/;HttpOnly;Domain=cors3.azurewebsites.net
Vary: Origin
X-Powered-By: ASP.NET
要求標頭
Accept: */*
Accept-Encoding: gzip, deflate, br
Accept-Language: en-US,en;q=0.9
Access-Control-Request-Headers: content-type
Access-Control-Request-Method: DELETE
Connection: keep-alive
Host: cors3.azurewebsites.net
Origin: https://cors1.azurewebsites.net
Referer: https://cors1.azurewebsites.net/test?number=2
Sec-Fetch-Dest: empty
Sec-Fetch-Mode: cors
Sec-Fetch-Site: cross-site
User-Agent: Mozilla/5.0
在上述回應 標頭中,伺服器會在回應中設定 Access-Control-Allow-Origin 標頭。 https://cors1.azurewebsites.net
此標頭的值符合 Origin
要求中的標頭。
如果 AllowAnyOrigin 呼叫 , Access-Control-Allow-Origin: *
則會傳回 萬用字元值。 AllowAnyOrigin
允許任何原始來源。
如果回應不包含 Access-Control-Allow-Origin
標頭,跨原始來源要求就會失敗。 具體而言,瀏覽器不允許要求。 即使伺服器傳回成功的回應,瀏覽器也不會將回應提供給用戶端應用程式。
HTTP 重新導向至 HTTPS 會導致 CORS 預檢要求上ERR_INVALID_REDIRECT
使用 HTTP 重新導向至 HTTPS UseHttpsRedirection 的端點要求會失敗 ERR_INVALID_REDIRECT on the CORS preflight request
。
API 專案可以拒絕 HTTP 要求,而不是用來 UseHttpsRedirection
將要求重新導向至 HTTPS。
IIS 中的 CORS
部署至 IIS 時,如果伺服器未設定為允許匿名存取,CORS 必須在 Windows 驗證之前執行。 若要支援此案例,必須安裝並設定應用程式的 IIS CORS 模組 。
測試 CORS
範例下載有程式碼可測試 CORS。 請參閱如何下載。 此範例是已新增 Pages 的 Razor API 專案:
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddCors(options =>
{
options.AddPolicy(name: "MyPolicy",
policy =>
{
policy.WithOrigins("http://example.com",
"http://www.contoso.com",
"https://cors1.azurewebsites.net",
"https://cors3.azurewebsites.net",
"https://localhost:44398",
"https://localhost:5001")
.WithMethods("PUT", "DELETE", "GET");
});
});
builder.Services.AddControllers();
builder.Services.AddRazorPages();
var app = builder.Build();
app.UseHttpsRedirection();
app.UseStaticFiles();
app.UseRouting();
app.UseCors();
app.UseAuthorization();
app.MapControllers();
app.MapRazorPages();
app.Run();
警告
WithOrigins("https://localhost:<port>");
應該只用于測試類似 下載範例程式碼的範例應用程式。
下列 ValuesController
提供用於測試的端點:
[EnableCors("MyPolicy")]
[Route("api/[controller]")]
[ApiController]
public class ValuesController : ControllerBase
{
// GET api/values
[HttpGet]
public IActionResult Get() =>
ControllerContext.MyDisplayRouteInfo();
// GET api/values/5
[HttpGet("{id}")]
public IActionResult Get(int id) =>
ControllerContext.MyDisplayRouteInfo(id);
// PUT api/values/5
[HttpPut("{id}")]
public IActionResult Put(int id) =>
ControllerContext.MyDisplayRouteInfo(id);
// GET: api/values/GetValues2
[DisableCors]
[HttpGet("{action}")]
public IActionResult GetValues2() =>
ControllerContext.MyDisplayRouteInfo();
}
MyDisplayRouteInfo 是由 Rick.Docs.Samples.RouteInfo NuGet 套件提供,並顯示路由資訊。
使用下列其中一種方法來測試上述範例程式碼:
- 在 使用已部署的範例應用程式 https://cors3.azurewebsites.net/ 。 不需要下載範例。
- 使用 的預設 URL
https://localhost:5001
執行範例dotnet run
。 - 從 Visual Studio 執行範例,並將埠設定為 44398,以取得 的
https://localhost:44398
URL。
搭配 F12 工具使用瀏覽器:
選取 [ 值] 按鈕,然後檢閱 [ 網路 ] 索引標籤中的標頭。
選取 PUT 測試 按鈕。 如需顯示 OPTIONS 要求的指示,請參閱 顯示 OPTIONS 要求。 PUT 測試會建立兩個要求:OPTIONS 預檢要求和 PUT 要求。
GetValues2 [DisableCors]
選取按鈕以觸發失敗的 CORS 要求。 如檔中所述,回應會傳回 200 成功,但不會提出 CORS 要求。 選取 [主控台] 索引標籤以查看 CORS 錯誤。 視瀏覽器而定,會顯示類似下列的錯誤:CORS 原則已封鎖從來源
'https://cors3.azurewebsites.net'
擷取'https://cors1.azurewebsites.net/api/values/GetValues2'
的存取:要求的資源上沒有 「存取控制-Allow-Origin」標頭。 如果不透明回應適合您的需求,請將要求的模式設定為 'no-cors' 以在停用 CORS 之下擷取資源。
啟用 CORS 的端點可以使用 curl、 Fiddler或 Postman等工具進行測試。 使用工具時,標頭所 Origin
指定的要求來源必須與接收要求的主機不同。 如果要求不是以標頭的值 Origin
為基礎的跨原始來源:
- 不需要 CORS 中介軟體來處理要求。
- 回應中不會傳回 CORS 標頭。
下列命令會使用 curl
發出具有資訊的 OPTIONS 要求:
curl -X OPTIONS https://cors3.azurewebsites.net/api/TodoItems2/5 -i
使用 [EnableCors] 屬性和 RequireCors 方法測試 CORS
請考慮使用 端點路由 的下列程式碼,以使用 RequireCors
來啟用每個端點的 CORS:
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddCors(options =>
{
options.AddPolicy(name: "MyPolicy",
policy =>
{
policy.WithOrigins("http://example.com",
"http://www.contoso.com",
"https://cors1.azurewebsites.net",
"https://cors3.azurewebsites.net",
"https://localhost:44398",
"https://localhost:5001")
.WithMethods("PUT", "DELETE", "GET");
});
});
builder.Services.AddControllers();
builder.Services.AddRazorPages();
var app = builder.Build();
app.UseHttpsRedirection();
app.UseStaticFiles();
app.UseRouting();
app.UseCors();
app.UseAuthorization();
app.UseEndpoints(endpoints =>
{
endpoints.MapGet("/echo",
context => context.Response.WriteAsync("echo"))
.RequireCors("MyPolicy");
endpoints.MapControllers();
endpoints.MapRazorPages();
});
app.Run();
請注意,只有 /echo
端點使用 RequireCors
來允許使用指定的原則的跨原始來源要求。 下列控制器會使用 [EnableCors] 屬性來啟用 CORS。
下列 TodoItems1Controller
提供用於測試的端點:
[Route("api/[controller]")]
[ApiController]
public class TodoItems1Controller : ControllerBase
{
// PUT: api/TodoItems1/5
[HttpPut("{id}")]
public IActionResult PutTodoItem(int id) {
if (id < 1) {
return Content($"ID = {id}");
}
return ControllerContext.MyDisplayRouteInfo(id);
}
// Delete: api/TodoItems1/5
[HttpDelete("{id}")]
public IActionResult MyDelete(int id) =>
ControllerContext.MyDisplayRouteInfo(id);
// GET: api/TodoItems1
[HttpGet]
public IActionResult GetTodoItems() =>
ControllerContext.MyDisplayRouteInfo();
[EnableCors("MyPolicy")]
[HttpGet("{action}")]
public IActionResult GetTodoItems2() =>
ControllerContext.MyDisplayRouteInfo();
// Delete: api/TodoItems1/MyDelete2/5
[EnableCors("MyPolicy")]
[HttpDelete("{action}/{id}")]
public IActionResult MyDelete2(int id) =>
ControllerContext.MyDisplayRouteInfo(id);
}
刪除 [EnableCors]和GET [EnableCors]按鈕會成功,因為端點具有 [EnableCors]
並回應預檢要求。 其他端點失敗。 GET按鈕失敗,因為JavaScript會傳送:
headers: {
"Content-Type": "x-custom-header"
},
下列 TodoItems2Controller
提供類似的端點,但包含回應 OPTIONS 要求的明確程式碼:
[Route("api/[controller]")]
[ApiController]
public class TodoItems2Controller : ControllerBase
{
// OPTIONS: api/TodoItems2/5
[HttpOptions("{id}")]
public IActionResult PreflightRoute(int id)
{
return NoContent();
}
// OPTIONS: api/TodoItems2
[HttpOptions]
public IActionResult PreflightRoute()
{
return NoContent();
}
[HttpPut("{id}")]
public IActionResult PutTodoItem(int id)
{
if (id < 1)
{
return BadRequest();
}
return ControllerContext.MyDisplayRouteInfo(id);
}
// [EnableCors] // Not needed as OPTIONS path provided
[HttpDelete("{id}")]
public IActionResult MyDelete(int id) =>
ControllerContext.MyDisplayRouteInfo(id);
[EnableCors("MyPolicy")] // Rquired for this path
[HttpGet]
public IActionResult GetTodoItems() =>
ControllerContext.MyDisplayRouteInfo();
[HttpGet("{action}")]
public IActionResult GetTodoItems2() =>
ControllerContext.MyDisplayRouteInfo();
[EnableCors("MyPolicy")] // Rquired for this path
[HttpDelete("{action}/{id}")]
public IActionResult MyDelete2(int id) =>
ControllerContext.MyDisplayRouteInfo(id);
}
從已部署範例 的測試頁面測試 上述程式碼。 在 [ 控制器 ] 下拉式清單中,選取 [ 預檢] ,然後選取 [ 設定控制器]。 所有對端點的 TodoItems2Controller
CORS 呼叫都成功。
其他資源
作者:Rick Anderson 與 Kirk Larkin
本文說明如何在 ASP.NET Core應用程式中啟用 CORS。
瀏覽器安全性可防止網頁向不同網域提出要求,而不是提供網頁的網域。 此限制稱為 相同原始來源原則。 相同原始原則可防止惡意網站從另一個網站讀取敏感性資料。 有時候,您可能想要允許其他網站對您的應用程式提出跨原始來源要求。 如需詳細資訊,請參閱 Mozilla CORS 文章。
跨原始來源資源分享 (CORS) :
- 這是一個 W3C 標準,可讓伺服器放寬相同原始原則。
- 不是安全性功能,CORS 會放寬安全性。 藉由允許 CORS,API 並不更安全。 如需詳細資訊,請參閱 CORS 的運作方式。
- 允許伺服器明確允許某些跨原始來源要求,同時拒絕其他要求。
- 比先前的技術更安全且更有彈性,例如JS ONP。
檢視或下載範例程式碼 \(英文\) (如何下載)
相同原點
如果兩個 URL 具有相同的配置、主機和埠, (RFC 6454) ,則具有相同的來源。
這兩個 URL 的原點相同:
https://example.com/foo.html
https://example.com/bar.html
這些 URL 的原點與前兩個 URL 不同:
https://example.net
:不同的網域https://www.example.com/foo.html
:不同的子域http://example.com/foo.html
:不同的配置https://example.com:9000/foo.html
:不同的埠
啟用 CORS
有三種方式可以啟用 CORS:
- 在中介軟體中使用 具名原則 或 預設原則。
- 使用 端點路由。
- 使用 [EnableCors] 屬性。
使用 [EnableCors] 屬性搭配具名原則可提供限制支援 CORS 之端點的最佳控制項。
警告
UseCors 必須以正確的順序呼叫。 如需詳細資訊,請參閱 中介軟體訂單。 例如, UseCors
在使用 UseResponseCaching
之前 UseResponseCaching ,必須先呼叫 。
下列各節會詳細說明每個方法。
具有具名原則和中介軟體的 CORS
CORS 中介軟體會處理跨原始來源要求。 下列程式碼會將 CORS 原則套用至具有指定來源的所有應用程式端點:
var MyAllowSpecificOrigins = "_myAllowSpecificOrigins";
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddCors(options =>
{
options.AddPolicy(name: MyAllowSpecificOrigins,
policy =>
{
policy.WithOrigins("http://example.com",
"http://www.contoso.com");
});
});
// services.AddResponseCaching();
builder.Services.AddControllers();
var app = builder.Build();
app.UseHttpsRedirection();
app.UseStaticFiles();
app.UseRouting();
app.UseCors(MyAllowSpecificOrigins);
app.UseAuthorization();
app.MapControllers();
app.Run();
上述程式碼:
- 將原則名稱設定為
_myAllowSpecificOrigins
。 原則名稱是任意的。 - UseCors呼叫擴充方法,並指定
_myAllowSpecificOrigins
CORS 原則。UseCors
會新增 CORS 中介軟體。 的呼叫UseCors
必須放在 之後UseRouting
,但在 之前UseAuthorization
。 如需詳細資訊,請參閱 中介軟體訂單。 - 使用Lambda 運算式呼叫 AddCors 。 Lambda 會接受 CorsPolicyBuilder 物件。 本文稍後會說明組態選項,例如
WithOrigins
。 _myAllowSpecificOrigins
啟用所有控制器端點的 CORS 原則。 請參閱 端點路由 ,將 CORS 原則套用至特定端點。- 使用回應快取中介軟體時,請在 之前 UseResponseCaching 呼叫 UseCors 。
使用端點路由時,CORS中介軟體必須設定為在 對 和 UseEndpoints
的呼叫 UseRouting
之間執行。
如需與上述程式碼類似的測試程式碼的指示,請參閱 測試 CORS 。
方法呼叫會將 AddCors CORS 服務新增至應用程式的服務容器:
var MyAllowSpecificOrigins = "_myAllowSpecificOrigins";
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddCors(options =>
{
options.AddPolicy(name: MyAllowSpecificOrigins,
policy =>
{
policy.WithOrigins("http://example.com",
"http://www.contoso.com");
});
});
// services.AddResponseCaching();
builder.Services.AddControllers();
var app = builder.Build();
app.UseHttpsRedirection();
app.UseStaticFiles();
app.UseRouting();
app.UseCors(MyAllowSpecificOrigins);
app.UseAuthorization();
app.MapControllers();
app.Run();
如需詳細資訊,請參閱本檔中 的 CORS 原則選項 。
CorsPolicyBuilder方法可以鏈結,如下列程式碼所示:
var MyAllowSpecificOrigins = "_myAllowSpecificOrigins";
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddCors(options =>
{
options.AddPolicy(MyAllowSpecificOrigins,
policy =>
{
policy.WithOrigins("http://example.com",
"http://www.contoso.com")
.AllowAnyHeader()
.AllowAnyMethod();
});
});
builder.Services.AddControllers();
var app = builder.Build();
app.UseHttpsRedirection();
app.UseStaticFiles();
app.UseRouting();
app.UseCors(MyAllowSpecificOrigins);
app.UseAuthorization();
app.MapControllers();
app.Run();
注意:指定的 URL 不得 包含尾端斜線 (/
) 。 如果 URL 以 /
終止,則比較會 false
傳回 ,而且不會傳回任何標頭。
UseCors 和 UseStaticFiles 順序
一般而言, UseStaticFiles
會在 之前 UseCors
呼叫 。 使用 JavaScript 跨網站擷取靜態檔案的應用程式必須在 之前 UseStaticFiles
呼叫 UseCors
。
具有預設原則和中介軟體的 CORS
下列醒目提示的程式碼會啟用預設 CORS 原則:
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddCors(options =>
{
options.AddDefaultPolicy(
policy =>
{
policy.WithOrigins("http://example.com",
"http://www.contoso.com");
});
});
builder.Services.AddControllers();
var app = builder.Build();
app.UseHttpsRedirection();
app.UseStaticFiles();
app.UseRouting();
app.UseCors();
app.UseAuthorization();
app.MapControllers();
app.Run();
上述程式碼會將預設 CORS 原則套用至所有控制器端點。
啟用具有端點路由的 Cors
使用 RequireCors
的每個端點啟用 CORS不支援自動預檢要求。如需詳細資訊,請參閱此 GitHub 問題和測試具有端點路由和 [HttpOptions] 的 CORS。
使用端點路由時,您可以使用一組擴充方法,依端點 RequireCors 啟用 CORS:
var MyAllowSpecificOrigins = "_myAllowSpecificOrigins";
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddCors(options =>
{
options.AddPolicy(name: MyAllowSpecificOrigins,
policy =>
{
policy.WithOrigins("http://example.com",
"http://www.contoso.com");
});
});
builder.Services.AddControllers();
builder.Services.AddRazorPages();
var app = builder.Build();
app.UseHttpsRedirection();
app.UseStaticFiles();
app.UseRouting();
app.UseCors();
app.UseAuthorization();
app.UseEndpoints(endpoints =>
{
endpoints.MapGet("/echo",
context => context.Response.WriteAsync("echo"))
.RequireCors(MyAllowSpecificOrigins);
endpoints.MapControllers()
.RequireCors(MyAllowSpecificOrigins);
endpoints.MapGet("/echo2",
context => context.Response.WriteAsync("echo2"));
endpoints.MapRazorPages();
});
app.Run();
在上述程式碼中:
app.UseCors
啟用 CORS 中介軟體。 因為尚未設定預設原則,app.UseCors()
所以單獨不會啟用 CORS。/echo
和 控制器端點允許使用指定的原則進行跨原始來源要求。- 和 Pages 端點不允許跨原始來源要求,因為未指定任何預設原則。 Razor
/echo2
[DisableCors]屬性不會停用端點路由所 RequireCors
啟用的 CORS。
如需測試類似上述程式碼的指示,請參閱 使用端點路由測試 CORS 和 [HttpOptions ]。
使用屬性啟用 CORS
使用 [EnableCors] 屬性啟用 CORS,並將具名原則套用至僅需要 CORS 的端點提供最佳控制項。
[EnableCors]屬性提供全域套用 CORS 的替代方案。 屬性 [EnableCors]
會啟用所選端點的 CORS,而不是所有端點:
[EnableCors]
會指定預設原則。[EnableCors("{Policy String}")]
指定具名原則。
屬性 [EnableCors]
可以套用至:
- Razor 網頁
PageModel
- 控制器
- 控制器動作方法
不同的原則可以套用至具有 屬性的控制器、頁面模型或動作方法 [EnableCors]
。 當屬性 [EnableCors]
套用至控制器、頁面模型或動作方法,並在中介軟體中啟用 CORS 時,會套用 這兩 個原則。 建議您不要合併原則。 使用 [EnableCors]
屬性或中介軟體,而不是在同一個應用程式中。
下列程式碼會將不同的原則套用至每個方法:
[Route("api/[controller]")]
[ApiController]
public class WidgetController : ControllerBase
{
// GET api/values
[EnableCors("AnotherPolicy")]
[HttpGet]
public ActionResult<IEnumerable<string>> Get()
{
return new string[] { "green widget", "red widget" };
}
// GET api/values/5
[EnableCors("Policy1")]
[HttpGet("{id}")]
public ActionResult<string> Get(int id)
{
return id switch
{
1 => "green widget",
2 => "red widget",
_ => NotFound(),
};
}
}
下列程式碼會建立兩個 CORS 原則:
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddCors(options =>
{
options.AddPolicy("Policy1",
policy =>
{
policy.WithOrigins("http://example.com",
"http://www.contoso.com");
});
options.AddPolicy("AnotherPolicy",
policy =>
{
policy.WithOrigins("http://www.contoso.com")
.AllowAnyHeader()
.AllowAnyMethod();
});
});
builder.Services.AddControllers();
var app = builder.Build();
app.UseHttpsRedirection();
app.UseRouting();
app.UseCors();
app.UseAuthorization();
app.MapControllers();
app.Run();
針對限制 CORS 要求的最精細控制:
- 搭配具名原則使用
[EnableCors("MyPolicy")]
。 - 請勿定義預設原則。
- 請勿使用 端點路由。
下一節中的程式碼符合上述清單。
如需與上述程式碼類似的測試程式碼的指示,請參閱 測試 CORS 。
停用 CORS
[DisableCors]屬性不會停用端點路由啟用的 CORS。
下列程式碼會定義 CORS 原則 "MyPolicy"
:
var MyAllowSpecificOrigins = "_myAllowSpecificOrigins";
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddCors(options =>
{
options.AddPolicy(name: "MyPolicy",
policy =>
{
policy.WithOrigins("http://example.com",
"http://www.contoso.com")
.WithMethods("PUT", "DELETE", "GET");
});
});
builder.Services.AddControllers();
builder.Services.AddRazorPages();
var app = builder.Build();
app.UseHttpsRedirection();
app.UseStaticFiles();
app.UseRouting();
app.UseCors();
app.UseAuthorization();
app.MapControllers();
app.MapRazorPages();
app.Run();
下列程式碼會停用動作的 GetValues2
CORS:
[EnableCors("MyPolicy")]
[Route("api/[controller]")]
[ApiController]
public class ValuesController : ControllerBase
{
// GET api/values
[HttpGet]
public IActionResult Get() =>
ControllerContext.MyDisplayRouteInfo();
// GET api/values/5
[HttpGet("{id}")]
public IActionResult Get(int id) =>
ControllerContext.MyDisplayRouteInfo(id);
// PUT api/values/5
[HttpPut("{id}")]
public IActionResult Put(int id) =>
ControllerContext.MyDisplayRouteInfo(id);
// GET: api/values/GetValues2
[DisableCors]
[HttpGet("{action}")]
public IActionResult GetValues2() =>
ControllerContext.MyDisplayRouteInfo();
}
上述程式碼:
- 不會啟用具有 端點路由的 CORS。
- 不會定義 預設 CORS 原則。
- 使用 [EnableCors (「MyPolicy」) ] 來啟用
"MyPolicy"
控制器的 CORS 原則。 - 停用 方法的
GetValues2
CORS。
如需測試上述程式碼的指示,請參閱 測試 CORS 。
CORS 原則選項
本節說明可在 CORS 原則中設定的各種選項:
AddPolicy 在 中 Program.cs
呼叫 。 對於某些選項,請先閱讀 CORS 的運作 方式一節可能會很有説明。
設定允許的來源
AllowAnyOrigin:允許來自所有來源的 CORS 要求,其中包含任何配置 (http
或 https
) 。 AllowAnyOrigin
不安全,因為 任何網站 都可以對應用程式提出跨原始來源要求。
注意
指定 AllowAnyOrigin
和 AllowCredentials
是不安全的組態,而且可能會導致跨網站偽造要求。 當應用程式使用這兩種方法設定時,CORS 服務會傳回無效 CORS 回應。
AllowAnyOrigin
會影響預檢要求和 Access-Control-Allow-Origin
標頭。 如需詳細資訊,請參閱 預檢要求 一節。
SetIsOriginAllowedToAllowWildcardSubdomains:將原則 IsOriginAllowed 的 屬性設定為函式,以允許原始來源在評估是否允許來源時比對已設定的萬用字元定義域。
var MyAllowSpecificOrigins = "_MyAllowSubdomainPolicy";
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddCors(options =>
{
options.AddPolicy(name: MyAllowSpecificOrigins,
policy =>
{
policy.WithOrigins("https://*.example.com")
.SetIsOriginAllowedToAllowWildcardSubdomains();
});
});
builder.Services.AddControllers();
var app = builder.Build();
設定允許的 HTTP 方法
- 允許任何 HTTP 方法:
- 影響預檢要求和
Access-Control-Allow-Methods
標頭。 如需詳細資訊,請參閱 預檢要求 一節。
設定允許的要求標頭
若要允許在 CORS 要求中傳送特定標頭,稱為 作者要求標頭,請呼叫 WithHeaders 並指定允許的標頭:
using Microsoft.Net.Http.Headers;
var MyAllowSpecificOrigins = "_MyAllowSubdomainPolicy";
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddCors(options =>
{
options.AddPolicy(name: MyAllowSpecificOrigins,
policy =>
{
policy.WithOrigins("http://example.com")
.WithHeaders(HeaderNames.ContentType, "x-custom-header");
});
});
builder.Services.AddControllers();
var app = builder.Build();
若要允許所有 作者要求標頭,請呼叫 AllowAnyHeader :
var MyAllowSpecificOrigins = "_MyAllowSubdomainPolicy";
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddCors(options =>
{
options.AddPolicy(name: MyAllowSpecificOrigins,
policy =>
{
policy.WithOrigins("https://*.example.com")
.AllowAnyHeader();
});
});
builder.Services.AddControllers();
var app = builder.Build();
AllowAnyHeader
會影響預檢要求和 Access-Control-Request-Headers 標頭。 如需詳細資訊,請參閱 預檢要求 一節。
只有在 所傳送 Access-Control-Request-Headers
的標頭完全符合 中所 WithHeaders
說明的標頭時,CORS 中介軟體原則才會符合 所 WithHeaders
指定的特定標頭。
例如,請考慮設定的應用程式,如下所示:
app.UseCors(policy => policy.WithHeaders(HeaderNames.CacheControl));
CORS 中介軟體會拒絕具有下列要求標頭的預檢要求,因為 Content-Language
(HeaderNames.ContentLanguage) 未列在 WithHeaders
中:
Access-Control-Request-Headers: Cache-Control, Content-Language
應用程式會傳回 200 OK 回應,但不會傳回 CORS 標頭。 因此,瀏覽器不會嘗試跨原始來源要求。
設定公開的回應標頭
根據預設,瀏覽器不會將所有回應標頭公開給應用程式。 如需詳細資訊,請參閱 W3C 跨原始來源資源分享 (術語) :簡單回應標頭。
預設可用的回應標頭為:
Cache-Control
Content-Language
Content-Type
Expires
Last-Modified
Pragma
CORS 規格會呼叫這些標頭 的簡單回應標頭。 若要讓其他標頭可供應用程式使用,請呼叫 WithExposedHeaders :
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddCors(options =>
{
options.AddPolicy("MyExposeResponseHeadersPolicy",
policy =>
{
policy.WithOrigins("https://*.example.com")
.WithExposedHeaders("x-custom-header");
});
});
builder.Services.AddControllers();
var app = builder.Build();
跨原始來源要求中的認證
認證需要 CORS 要求中的特殊處理。 根據預設,瀏覽器不會傳送具有跨原始來源要求的認證。 認證包括 cookie s 和 HTTP 驗證配置。 若要傳送具有跨原始來源要求的認證,用戶端必須設定 XMLHttpRequest.withCredentials
為 true
。
直接使用 XMLHttpRequest
:
var xhr = new XMLHttpRequest();
xhr.open('get', 'https://www.example.com/api/test');
xhr.withCredentials = true;
使用 jQuery:
$.ajax({
type: 'get',
url: 'https://www.example.com/api/test',
xhrFields: {
withCredentials: true
}
});
使用 擷取 API:
fetch('https://www.example.com/api/test', {
credentials: 'include'
});
伺服器必須允許認證。 若要允許跨原始來源認證,請呼叫 AllowCredentials :
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddCors(options =>
{
options.AddPolicy("MyMyAllowCredentialsPolicy",
policy =>
{
policy.WithOrigins("http://example.com")
.AllowCredentials();
});
});
builder.Services.AddControllers();
var app = builder.Build();
HTTP 回應包含 Access-Control-Allow-Credentials
標頭,告知瀏覽器伺服器允許跨原始來源要求的認證。
如果瀏覽器傳送認證,但回應不包含有效的 Access-Control-Allow-Credentials
標頭,則瀏覽器不會向應用程式公開回應,而跨原始來源要求會失敗。
允許跨原始來源認證是安全性風險。 另一個網域的網站可以代表使用者將登入使用者的認證傳送給使用者代表的應用程式,而不需要使用者的知識。
CORS 規格也會指出,如果 Access-Control-Allow-Credentials
標頭存在,將原始來源設定為 "*"
(所有來源) 無效。
預檢要求
針對某些 CORS 要求,瀏覽器會先傳送額外的 OPTIONS 要求,再提出實際要求。 此要求稱為 預檢要求。 如果 下列所有 條件都成立,瀏覽器可以略過預檢要求:
- 要求方法是 GET、HEAD 或 POST。
- 應用程式不會設定 、
Accept-Language
、Content-Language
、Content-Type
或Last-Event-ID
以外的Accept
要求標頭。 Content-Type
如果設定標頭,則具有下列其中一個值:application/x-www-form-urlencoded
multipart/form-data
text/plain
針對用戶端要求所設定的要求標頭規則會套用至應用程式在 物件上呼叫 setRequestHeader
所設定的 XMLHttpRequest
標頭。 CORS 規格會呼叫這些標頭 作者要求標頭。 此規則不適用於瀏覽器可以設定的標頭,例如 User-Agent
、 Host
或 Content-Length
。
以下是類似本檔之 [測試 CORS] 區段中[Put test]按鈕所提出預檢要求的範例回應。
General:
Request URL: https://cors3.azurewebsites.net/api/values/5
Request Method: OPTIONS
Status Code: 204 No Content
Response Headers:
Access-Control-Allow-Methods: PUT,DELETE,GET
Access-Control-Allow-Origin: https://cors1.azurewebsites.net
Server: Microsoft-IIS/10.0
Set-Cookie: ARRAffinity=8f8...8;Path=/;HttpOnly;Domain=cors1.azurewebsites.net
Vary: Origin
Request Headers:
Accept: */*
Accept-Encoding: gzip, deflate, br
Accept-Language: en-US,en;q=0.9
Access-Control-Request-Method: PUT
Connection: keep-alive
Host: cors3.azurewebsites.net
Origin: https://cors1.azurewebsites.net
Referer: https://cors1.azurewebsites.net/
Sec-Fetch-Dest: empty
Sec-Fetch-Mode: cors
Sec-Fetch-Site: cross-site
User-Agent: Mozilla/5.0
預檢要求會使用 HTTP OPTIONS 方法。 它可能包含下列標頭:
- Access-Control-Request-Method:將用於實際要求的 HTTP 方法。
- Access-Control-Request-Headers:應用程式在實際要求上設定的要求標頭清單。 如先前所述,這不包含瀏覽器設定的標頭,例如
User-Agent
。 - Access-Control-Allow-Methods
如果預檢要求遭到拒絕,應用程式會 200 OK
傳迴響應,但未設定 CORS 標頭。 因此,瀏覽器不會嘗試跨原始來源要求。 For an example of a denied preflight request, see the Test CORS section of this document.
使用 F12 工具時,主控台應用程式會顯示類似下列其中一項的錯誤,視瀏覽器而定:
- Firefox:已封鎖跨原始來源要求:相同的原始原則不允許讀取位於
https://cors1.azurewebsites.net/api/TodoItems1/MyDelete2/5
的遠端資源。 (原因:CORS 要求無法成功) 。 深入了解 - 以Chromium為基礎:CORS 原則已封鎖從來源 ' https://cors1.azurewebsites.net/api/TodoItems1/MyDelete2/5https://cors3.azurewebsites.net ' 擷取的存取權:回應預檢要求不會通過存取控制檢查:要求的資源上沒有 'Access-Control-Allow-Origin' 標頭存在。 如果不透明回應適合您的需求,請將要求的模式設定為 'no-cors' 以在停用 CORS 之下擷取資源。
若要允許特定標頭,請呼叫 WithHeaders :
using Microsoft.Net.Http.Headers;
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddCors(options =>
{
options.AddPolicy("MyAllowHeadersPolicy",
policy =>
{
policy.WithOrigins("http://example.com")
.WithHeaders(HeaderNames.ContentType, "x-custom-header");
});
});
builder.Services.AddControllers();
var app = builder.Build();
若要允許所有 作者要求標頭,請呼叫 AllowAnyHeader :
using Microsoft.Net.Http.Headers;
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddCors(options =>
{
options.AddPolicy("MyAllowAllHeadersPolicy",
policy =>
{
policy.WithOrigins("https://*.example.com")
.AllowAnyHeader();
});
});
builder.Services.AddControllers();
var app = builder.Build();
瀏覽器在設定 Access-Control-Request-Headers
的方式中不一致。 如果下列任一項:
- 標頭設定為以外的任何專案
"*"
- AllowAnyHeader 稱為:至少
Accept
包含 、Content-Type
和Origin
,以及您想要支援的任何自訂標頭。
自動預檢要求程式碼
套用 CORS 原則時:
- 在 中
Program.cs
呼叫 ,以全域方式呼叫app.UseCors
。 [EnableCors]
使用 屬性。
ASP.NET Core回應預檢選項要求。
目前使用 RequireCors
的每個端點啟用 CORS 不支援 自動預檢要求。
本檔的 [測試 CORS ] 區段示範此行為。
[HttpOptions] 預檢要求的屬性
使用適當的原則啟用 CORS 時,ASP.NET Core通常會自動回應 CORS 預檢要求。 在某些情況下,這可能不是這種情況。 例如,搭配 端點路由使用 CORS。
下列程式碼會使用 [HttpOptions] 屬性來建立 OPTIONS 要求的端點:
[Route("api/[controller]")]
[ApiController]
public class TodoItems2Controller : ControllerBase
{
// OPTIONS: api/TodoItems2/5
[HttpOptions("{id}")]
public IActionResult PreflightRoute(int id)
{
return NoContent();
}
// OPTIONS: api/TodoItems2
[HttpOptions]
public IActionResult PreflightRoute()
{
return NoContent();
}
[HttpPut("{id}")]
public IActionResult PutTodoItem(int id)
{
if (id < 1)
{
return BadRequest();
}
return ControllerContext.MyDisplayRouteInfo(id);
}
如需測試上述程式碼的指示,請參閱 使用端點路由測試 CORS 和 [HttpOptions ]。
設定預檢到期時間
Access-Control-Max-Age
標頭會指定快取預檢要求回應的時間長度。 若要設定此標頭,請呼叫 SetPreflightMaxAge :
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddCors(options =>
{
options.AddPolicy("MySetPreflightExpirationPolicy",
policy =>
{
policy.WithOrigins("http://example.com")
.SetPreflightMaxAge(TimeSpan.FromSeconds(2520));
});
});
builder.Services.AddControllers();
var app = builder.Build();
CORS 的運作方式
本節說明在 HTTP 訊息層級的 CORS 要求中會發生什麼情況。
- CORS 不是 安全性功能。 CORS 是 W3C 標準,可讓伺服器放寬相同的原始來源原則。
- 例如,惡意執行者可能會針對您的網站使用 跨網站腳本 (XSS) ,並對其已啟用 CORS 的網站執行跨網站要求來竊取資訊。
- 藉由允許 CORS,API 並不更安全。
- 用戶端 (瀏覽器) 強制執行 CORS。 伺服器會執行要求並傳迴響應,它是傳回錯誤的用戶端,並封鎖回應。 例如,下列任何工具都會顯示伺服器回應:
- Fiddler
- Postman
- .NET HttpClient
- 在網址列中輸入 URL,以使用網頁瀏覽器。
- 用戶端 (瀏覽器) 強制執行 CORS。 伺服器會執行要求並傳迴響應,它是傳回錯誤的用戶端,並封鎖回應。 例如,下列任何工具都會顯示伺服器回應:
- 這是讓伺服器能夠允許瀏覽器執行跨原始來源 XHR 或 擷取 API 要求的方式,否則會遭到禁止。
- 沒有 CORS 的瀏覽器無法執行跨原始來源要求。 在 CORS 之前,JS ONP是用來規避這項限制。 JSONP 不會使用 XHR,它會使用
<script>
標記來接收回應。 允許跨原始來源載入腳本。
- 沒有 CORS 的瀏覽器無法執行跨原始來源要求。 在 CORS 之前,JS ONP是用來規避這項限制。 JSONP 不會使用 XHR,它會使用
CORS 規格引進了數個新的 HTTP 標頭,可啟用跨原始來源要求。 如果瀏覽器支援 CORS,它會針對跨原始來源要求自動設定這些標頭。 啟用 CORS 不需要自訂 JavaScript 程式碼。
以下是從 [ 值 ] 測試按鈕到 https://cors1.azurewebsites.net/api/values
的跨原始來源要求範例。 標頭 Origin
:
- 提供提出要求之網站的網域。
- 為必要專案,且必須與主機不同。
一般標頭
Request URL: https://cors1.azurewebsites.net/api/values
Request Method: GET
Status Code: 200 OK
回應標頭
Content-Encoding: gzip
Content-Type: text/plain; charset=utf-8
Server: Microsoft-IIS/10.0
Set-Cookie: ARRAffinity=8f...;Path=/;HttpOnly;Domain=cors1.azurewebsites.net
Transfer-Encoding: chunked
Vary: Accept-Encoding
X-Powered-By: ASP.NET
要求標頭
Accept: */*
Accept-Encoding: gzip, deflate, br
Accept-Language: en-US,en;q=0.9
Connection: keep-alive
Host: cors1.azurewebsites.net
Origin: https://cors3.azurewebsites.net
Referer: https://cors3.azurewebsites.net/
Sec-Fetch-Dest: empty
Sec-Fetch-Mode: cors
Sec-Fetch-Site: cross-site
User-Agent: Mozilla/5.0 ...
在 OPTIONS
要求中,伺服器會在回應中設定Response 標頭Access-Control-Allow-Origin: {allowed origin}
。 例如,已部署 的範例Delete [EnableCors] 按鈕 OPTIONS
要求包含下列標頭:
一般標頭
Request URL: https://cors3.azurewebsites.net/api/TodoItems2/MyDelete2/5
Request Method: OPTIONS
Status Code: 204 No Content
回應標頭
Access-Control-Allow-Headers: Content-Type,x-custom-header
Access-Control-Allow-Methods: PUT,DELETE,GET,OPTIONS
Access-Control-Allow-Origin: https://cors1.azurewebsites.net
Server: Microsoft-IIS/10.0
Set-Cookie: ARRAffinity=8f...;Path=/;HttpOnly;Domain=cors3.azurewebsites.net
Vary: Origin
X-Powered-By: ASP.NET
要求標頭
Accept: */*
Accept-Encoding: gzip, deflate, br
Accept-Language: en-US,en;q=0.9
Access-Control-Request-Headers: content-type
Access-Control-Request-Method: DELETE
Connection: keep-alive
Host: cors3.azurewebsites.net
Origin: https://cors1.azurewebsites.net
Referer: https://cors1.azurewebsites.net/test?number=2
Sec-Fetch-Dest: empty
Sec-Fetch-Mode: cors
Sec-Fetch-Site: cross-site
User-Agent: Mozilla/5.0
在上述 Response 標頭中,伺服器會在回應中設定 Access-Control-Allow-Origin 標頭。 https://cors1.azurewebsites.net
此標頭的值符合 Origin
來自要求的標頭。
如果 AllowAnyOrigin 呼叫 , Access-Control-Allow-Origin: *
則會傳回萬用字元值。 AllowAnyOrigin
允許任何來源。
如果回應不包含 Access-Control-Allow-Origin
標頭,跨原始來源要求就會失敗。 具體而言,瀏覽器不允許要求。 即使伺服器傳回成功的回應,瀏覽器也不會將回應提供給用戶端應用程式。
HTTP 重新導向至 HTTPS 會導致 CORS 預檢要求ERR_INVALID_REDIRECT
使用以 HTTP 重新導向至 HTTPS UseHttpsRedirection 的端點要求會失敗, ERR_INVALID_REDIRECT on the CORS preflight request
API 專案可以拒絕 HTTP 要求,而不是使用 UseHttpsRedirection
將要求重新導向至 HTTPS。
顯示 OPTIONS 要求
根據預設,Chrome 和 Edge 瀏覽器不會在 F12 工具的網路索引標籤上顯示 OPTIONS 要求。 若要在這些瀏覽器中顯示 OPTIONS 要求:
chrome://flags/#out-of-blink-cors
或edge://flags/#out-of-blink-cors
- 停用 旗標。
- 重新 啟動。
Firefox 預設會顯示 OPTIONS 要求。
IIS 中的 CORS
部署至 IIS 時,如果伺服器未設定為允許匿名存取,CORS 必須在 Windows 驗證之前執行。 若要支援此案例,必須為應用程式安裝並設定 IIS CORS 模組 。
測試 CORS
範例下載具有測試 CORS 的程式碼。 請參閱如何下載。 此範例是已新增 Pages 的 Razor API 專案:
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddCors(options =>
{
options.AddPolicy(name: "MyPolicy",
policy =>
{
policy.WithOrigins("http://example.com",
"http://www.contoso.com",
"https://cors1.azurewebsites.net",
"https://cors3.azurewebsites.net",
"https://localhost:44398",
"https://localhost:5001")
.WithMethods("PUT", "DELETE", "GET");
});
});
builder.Services.AddControllers();
builder.Services.AddRazorPages();
var app = builder.Build();
app.UseHttpsRedirection();
app.UseStaticFiles();
app.UseRouting();
app.UseCors();
app.UseAuthorization();
app.MapControllers();
app.MapRazorPages();
app.Run();
警告
WithOrigins("https://localhost:<port>");
應該只用于測試類似 下載範例程式碼的範例應用程式。
下列 ValuesController
提供用於測試的端點:
[EnableCors("MyPolicy")]
[Route("api/[controller]")]
[ApiController]
public class ValuesController : ControllerBase
{
// GET api/values
[HttpGet]
public IActionResult Get() =>
ControllerContext.MyDisplayRouteInfo();
// GET api/values/5
[HttpGet("{id}")]
public IActionResult Get(int id) =>
ControllerContext.MyDisplayRouteInfo(id);
// PUT api/values/5
[HttpPut("{id}")]
public IActionResult Put(int id) =>
ControllerContext.MyDisplayRouteInfo(id);
// GET: api/values/GetValues2
[DisableCors]
[HttpGet("{action}")]
public IActionResult GetValues2() =>
ControllerContext.MyDisplayRouteInfo();
}
MyDisplayRouteInfo 是由 Rick.Docs.Samples.RouteInfo NuGet 套件提供,並顯示路由資訊。
使用下列其中一種方法來測試上述範例程式碼:
- 在 使用已部署的範例應用程式 https://cors3.azurewebsites.net/ 。 不需要下載範例。
- 使用 的預設 URL
https://localhost:5001
執行範例dotnet run
。 - 從 Visual Studio 執行範例,並將埠設定為 44398 作為 URL
https://localhost:44398
。
搭配 F12 工具使用瀏覽器:
選取 [ 值 ] 按鈕,然後檢閱 [ 網路 ] 索引標籤中的標頭。
選取 PUT 測試 按鈕。 如需顯示 OPTIONS 要求的指示,請參閱 顯示 OPTIONS 要求。 PUT 測試會建立兩個要求:OPTIONS 預檢要求和 PUT 要求。
GetValues2 [DisableCors]
選取按鈕以觸發失敗的 CORS 要求。 如檔中所述,回應會傳回 200 次成功,但不會提出 CORS 要求。 選取 [主控台] 索引卷 標以查看 CORS 錯誤。 視瀏覽器而定,會顯示類似下列的錯誤:CORS 原則已封鎖從
'https://cors1.azurewebsites.net/api/values/GetValues2'
來源'https://cors3.azurewebsites.net'
擷取的存取:要求的資源上沒有 'Access-Control-Allow-Origin' 標頭。 如果不透明回應適合您的需求,請將要求的模式設定為 'no-cors' 以在停用 CORS 之下擷取資源。
啟用 CORS 的端點可以使用 curl、 Fiddler或 Postman等工具進行測試。 使用工具時,標頭所 Origin
指定的要求來源必須與接收要求的主機不同。 如果要求不是以標頭的值 Origin
為基礎的跨原始來源:
- 不需要 CORS 中介軟體來處理要求。
- 回應中不會傳回 CORS 標頭。
下列命令會使用 curl
發出 OPTIONS 要求,其中包含資訊:
curl -X OPTIONS https://cors3.azurewebsites.net/api/TodoItems2/5 -i
使用端點路由和 [HttpOptions] 測試 CORS
目前使用 RequireCors
來啟用個別端點的 CORS不支援自動預檢要求。 請考慮使用 端點路由來啟用 CORS的下列程式碼:
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddCors(options =>
{
options.AddPolicy(name: "MyPolicy",
policy =>
{
policy.WithOrigins("http://example.com",
"http://www.contoso.com",
"https://cors1.azurewebsites.net",
"https://cors3.azurewebsites.net",
"https://localhost:44398",
"https://localhost:5001")
.WithMethods("PUT", "DELETE", "GET");
});
});
builder.Services.AddControllers();
builder.Services.AddRazorPages();
var app = builder.Build();
app.UseHttpsRedirection();
app.UseStaticFiles();
app.UseRouting();
app.UseCors();
app.UseAuthorization();
app.MapControllers();
app.MapRazorPages();
app.Run();
下列 TodoItems1Controller
提供用於測試的端點:
[Route("api/[controller]")]
[ApiController]
public class TodoItems1Controller : ControllerBase
{
// PUT: api/TodoItems1/5
[HttpPut("{id}")]
public IActionResult PutTodoItem(int id)
{
if (id < 1)
{
return Content($"ID = {id}");
}
return ControllerContext.MyDisplayRouteInfo(id);
}
// Delete: api/TodoItems1/5
[HttpDelete("{id}")]
public IActionResult MyDelete(int id) =>
ControllerContext.MyDisplayRouteInfo(id);
// GET: api/TodoItems1
[HttpGet]
public IActionResult GetTodoItems() =>
ControllerContext.MyDisplayRouteInfo();
[EnableCors]
[HttpGet("{action}")]
public IActionResult GetTodoItems2() =>
ControllerContext.MyDisplayRouteInfo();
// Delete: api/TodoItems1/MyDelete2/5
[EnableCors]
[HttpDelete("{action}/{id}")]
public IActionResult MyDelete2(int id) =>
ControllerContext.MyDisplayRouteInfo(id);
}
刪除 [EnableCors]和GET [EnableCors]按鈕會成功,因為端點具有 [EnableCors]
並回應預檢要求。 其他端點會失敗。 GET按鈕失敗,因為JavaScript會傳送:
headers: {
"Content-Type": "x-custom-header"
},
下列 TodoItems2Controller
提供類似的端點,但包含回應 OPTIONS 要求的明確程式碼:
[Route("api/[controller]")]
[ApiController]
public class TodoItems2Controller : ControllerBase
{
// OPTIONS: api/TodoItems2/5
[HttpOptions("{id}")]
public IActionResult PreflightRoute(int id)
{
return NoContent();
}
// OPTIONS: api/TodoItems2
[HttpOptions]
public IActionResult PreflightRoute()
{
return NoContent();
}
[HttpPut("{id}")]
public IActionResult PutTodoItem(int id)
{
if (id < 1)
{
return BadRequest();
}
return ControllerContext.MyDisplayRouteInfo(id);
}
// [EnableCors] // Not needed as OPTIONS path provided
[HttpDelete("{id}")]
public IActionResult MyDelete(int id) =>
ControllerContext.MyDisplayRouteInfo(id);
[EnableCors] // Rquired for this path
[HttpGet]
public IActionResult GetTodoItems() =>
ControllerContext.MyDisplayRouteInfo();
[HttpGet("{action}")]
public IActionResult GetTodoItems2() =>
ControllerContext.MyDisplayRouteInfo();
[EnableCors] // Rquired for this path
[HttpDelete("{action}/{id}")]
public IActionResult MyDelete2(int id) =>
ControllerContext.MyDisplayRouteInfo(id);
}
從已部署範例 的測試頁面測試 上述程式碼。 在 [ 控制器 ] 下拉式清單中,選取 [ 預檢] ,然後選取 [ 設定控制器]。 所有對端點的 TodoItems2Controller
CORS 呼叫都成功。
其他資源
作者:Rick Anderson 與 Kirk Larkin
本文說明如何在 ASP.NET Core應用程式中啟用 CORS。
瀏覽器安全性可防止網頁向不同網域提出要求,而不是提供網頁的網域。 此限制稱為 相同原始來源原則。 相同原始原則可防止惡意網站從另一個網站讀取敏感性資料。 有時候,您可能想要允許其他網站對您的應用程式提出跨原始來源要求。 如需詳細資訊,請參閱 Mozilla CORS 文章。
跨原始來源資源分享 (CORS) :
- 這是一個 W3C 標準,可讓伺服器放寬相同原始原則。
- 不是安全性功能,CORS 會放寬安全性。 藉由允許 CORS,API 並不更安全。 如需詳細資訊,請參閱 CORS 的運作方式。
- 允許伺服器明確允許某些跨原始來源要求,同時拒絕其他要求。
- 比先前的技術更安全且更有彈性,例如JS ONP。
檢視或下載範例程式碼 \(英文\) (如何下載)
相同原點
如果兩個 URL 具有相同的配置、主機和埠, (RFC 6454) ,則具有相同的來源。
這兩個 URL 的原點相同:
https://example.com/foo.html
https://example.com/bar.html
這些 URL 的原點與前兩個 URL 不同:
https://example.net
:不同的網域https://www.example.com/foo.html
:不同的子域http://example.com/foo.html
:不同的配置https://example.com:9000/foo.html
:不同的埠
啟用 CORS
有三種方式可以啟用 CORS:
- 在中介軟體中使用 具名原則 或 預設原則。
- 使用 端點路由。
- 使用 [EnableCors] 屬性。
使用 [EnableCors] 屬性搭配具名原則可提供限制支援 CORS 之端點的最佳控制項。
警告
UseCors 必須以正確的順序呼叫。 如需詳細資訊,請參閱 中介軟體訂單。 例如, UseCors
在使用 UseResponseCaching
之前 UseResponseCaching ,必須先呼叫 。
下列各節會詳細說明每個方法。
具有具名原則和中介軟體的 CORS
CORS 中介軟體會處理跨原始來源要求。 下列程式碼會將 CORS 原則套用至具有指定來源的所有應用程式端點:
public class Startup
{
readonly string MyAllowSpecificOrigins = "_myAllowSpecificOrigins";
public void ConfigureServices(IServiceCollection services)
{
services.AddCors(options =>
{
options.AddPolicy(name: MyAllowSpecificOrigins,
policy =>
{
policy.WithOrigins("http://example.com",
"http://www.contoso.com");
});
});
// services.AddResponseCaching();
services.AddControllers();
}
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
app.UseHttpsRedirection();
app.UseStaticFiles();
app.UseRouting();
app.UseCors(MyAllowSpecificOrigins);
// app.UseResponseCaching();
app.UseAuthorization();
app.UseEndpoints(endpoints =>
{
endpoints.MapControllers();
});
}
}
上述程式碼:
- 將原則名稱設定為
_myAllowSpecificOrigins
。 原則名稱是任意的。 - UseCors呼叫擴充方法,並指定
_myAllowSpecificOrigins
CORS 原則。UseCors
會新增 CORS 中介軟體。 的呼叫UseCors
必須放在 之後UseRouting
,但在 之前UseAuthorization
。 如需詳細資訊,請參閱 中介軟體訂單。 - 使用Lambda 運算式呼叫 AddCors 。 Lambda 會接受 CorsPolicyBuilder 物件。 本文稍後會說明組態選項,例如
WithOrigins
。 _myAllowSpecificOrigins
啟用所有控制器端點的 CORS 原則。 請參閱 端點路由 ,將 CORS 原則套用至特定端點。- 使用回應快取中介軟體時,請在 之前 UseResponseCaching 呼叫 UseCors 。
使用端點路由時,CORS中介軟體必須設定為在 對 和 UseEndpoints
的呼叫 UseRouting
之間執行。
如需與上述程式碼類似的測試程式碼的指示,請參閱 測試 CORS 。
方法呼叫會將 AddCors CORS 服務新增至應用程式的服務容器:
public class Startup
{
readonly string MyAllowSpecificOrigins = "_myAllowSpecificOrigins";
public void ConfigureServices(IServiceCollection services)
{
services.AddCors(options =>
{
options.AddPolicy(name: MyAllowSpecificOrigins,
policy =>
{
policy.WithOrigins("http://example.com",
"http://www.contoso.com");
});
});
// services.AddResponseCaching();
services.AddControllers();
}
如需詳細資訊,請參閱本檔中 的 CORS 原則選項 。
CorsPolicyBuilder方法可以鏈結,如下列程式碼所示:
public void ConfigureServices(IServiceCollection services)
{
services.AddCors(options =>
{
options.AddPolicy(MyAllowSpecificOrigins,
policy =>
{
policy.WithOrigins("http://example.com",
"http://www.contoso.com")
.AllowAnyHeader()
.AllowAnyMethod();
});
});
services.AddControllers();
}
注意:指定的 URL 不得 包含尾端斜線 (/
) 。 如果 URL 以 /
終止,則比較會 false
傳回 ,而且不會傳回任何標頭。
具有預設原則和中介軟體的 CORS
下列醒目提示的程式碼會啟用預設 CORS 原則:
public class Startup
{
public void ConfigureServices(IServiceCollection services)
{
services.AddCors(options =>
{
options.AddDefaultPolicy(
policy =>
{
policy.WithOrigins("http://example.com",
"http://www.contoso.com");
});
});
services.AddControllers();
}
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
app.UseHttpsRedirection();
app.UseStaticFiles();
app.UseRouting();
app.UseCors();
app.UseAuthorization();
app.UseEndpoints(endpoints =>
{
endpoints.MapControllers();
});
}
}
上述程式碼會將預設 CORS 原則套用至所有控制器端點。
啟用具有端點路由的 Cors
使用 RequireCors
的每個端點啟用 CORS不支援自動預檢要求。如需詳細資訊,請參閱此 GitHub 問題和測試具有端點路由和 [HttpOptions] 的 CORS。
使用端點路由時,您可以使用一組擴充方法,依端點 RequireCors 啟用 CORS:
public class Startup
{
readonly string MyAllowSpecificOrigins = "_myAllowSpecificOrigins";
public void ConfigureServices(IServiceCollection services)
{
services.AddCors(options =>
{
options.AddPolicy(name: MyAllowSpecificOrigins,
policy =>
{
policy.WithOrigins("http://example.com",
"http://www.contoso.com");
});
});
services.AddControllers();
services.AddRazorPages();
}
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
app.UseHttpsRedirection();
app.UseStaticFiles();
app.UseRouting();
app.UseCors();
app.UseAuthorization();
app.UseEndpoints(endpoints =>
{
endpoints.MapGet("/echo",
context => context.Response.WriteAsync("echo"))
.RequireCors(MyAllowSpecificOrigins);
endpoints.MapControllers()
.RequireCors(MyAllowSpecificOrigins);
endpoints.MapGet("/echo2",
context => context.Response.WriteAsync("echo2"));
endpoints.MapRazorPages();
});
}
}
在上述程式碼中:
app.UseCors
啟用 CORS 中介軟體。 因為尚未設定預設原則,app.UseCors()
所以單獨不會啟用 CORS。/echo
和 控制器端點允許使用指定的原則進行跨原始來源要求。- 和 Pages 端點不允許跨原始來源要求,因為未指定任何預設原則。 Razor
/echo2
[DisableCors]屬性不會停用端點路由所 RequireCors
啟用的 CORS。
如需測試類似上述程式碼的指示,請參閱 使用端點路由測試 CORS 和 [HttpOptions ]。
使用屬性啟用 CORS
使用 [EnableCors] 屬性啟用 CORS,並將具名原則套用至僅需要 CORS 的端點提供最佳控制項。
[EnableCors]屬性提供全域套用 CORS 的替代方案。 屬性 [EnableCors]
會啟用所選端點的 CORS,而不是所有端點:
[EnableCors]
會指定預設原則。[EnableCors("{Policy String}")]
指定具名原則。
屬性 [EnableCors]
可以套用至:
- Razor 網頁
PageModel
- 控制器
- 控制器動作方法
不同的原則可以套用至具有 屬性的控制器、頁面模型或動作方法 [EnableCors]
。 當屬性 [EnableCors]
套用至控制器、頁面模型或動作方法,並在中介軟體中啟用 CORS 時,會套用 這兩 個原則。 建議您不要合併原則。 使用 [EnableCors]
屬性或中介軟體,而不是在同一個應用程式中。
下列程式碼會將不同的原則套用至每個方法:
[Route("api/[controller]")]
[ApiController]
public class WidgetController : ControllerBase
{
// GET api/values
[EnableCors("AnotherPolicy")]
[HttpGet]
public ActionResult<IEnumerable<string>> Get()
{
return new string[] { "green widget", "red widget" };
}
// GET api/values/5
[EnableCors("Policy1")]
[HttpGet("{id}")]
public ActionResult<string> Get(int id)
{
return id switch
{
1 => "green widget",
2 => "red widget",
_ => NotFound(),
};
}
}
下列程式碼會建立兩個 CORS 原則:
public class Startup
{
public Startup(IConfiguration configuration)
{
Configuration = configuration;
}
public IConfiguration Configuration { get; }
public void ConfigureServices(IServiceCollection services)
{
services.AddCors(options =>
{
options.AddPolicy("Policy1",
policy =>
{
policy.WithOrigins("http://example.com",
"http://www.contoso.com");
});
options.AddPolicy("AnotherPolicy",
policy =>
{
policy.WithOrigins("http://www.contoso.com")
.AllowAnyHeader()
.AllowAnyMethod();
});
});
services.AddControllers();
}
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
app.UseHttpsRedirection();
app.UseRouting();
app.UseCors();
app.UseAuthorization();
app.UseEndpoints(endpoints =>
{
endpoints.MapControllers();
});
}
}
針對限制 CORS 要求的最精細控制:
- 搭配具名原則使用
[EnableCors("MyPolicy")]
。 - 請勿定義預設原則。
- 請勿使用 端點路由。
下一節中的程式碼符合上述清單。
如需與上述程式碼類似的測試程式碼的指示,請參閱 測試 CORS 。
停用 CORS
[DisableCors]屬性不會停用端點路由啟用的 CORS。
下列程式碼會定義 CORS 原則 "MyPolicy"
:
public class Startup
{
public void ConfigureServices(IServiceCollection services)
{
services.AddCors(options =>
{
options.AddPolicy(name: "MyPolicy",
policy =>
{
policy.WithOrigins("http://example.com",
"http://www.contoso.com")
.WithMethods("PUT", "DELETE", "GET");
});
});
services.AddControllers();
services.AddRazorPages();
}
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
app.UseHttpsRedirection();
app.UseStaticFiles();
app.UseRouting();
app.UseCors();
app.UseAuthorization();
app.UseEndpoints(endpoints =>
{
endpoints.MapControllers();
endpoints.MapRazorPages();
});
}
}
下列程式碼會停用動作的 GetValues2
CORS:
[EnableCors("MyPolicy")]
[Route("api/[controller]")]
[ApiController]
public class ValuesController : ControllerBase
{
// GET api/values
[HttpGet]
public IActionResult Get() =>
ControllerContext.MyDisplayRouteInfo();
// GET api/values/5
[HttpGet("{id}")]
public IActionResult Get(int id) =>
ControllerContext.MyDisplayRouteInfo(id);
// PUT api/values/5
[HttpPut("{id}")]
public IActionResult Put(int id) =>
ControllerContext.MyDisplayRouteInfo(id);
// GET: api/values/GetValues2
[DisableCors]
[HttpGet("{action}")]
public IActionResult GetValues2() =>
ControllerContext.MyDisplayRouteInfo();
}
上述程式碼:
- 不會啟用具有 端點路由的 CORS。
- 不會定義 預設 CORS 原則。
- 使用 [EnableCors (「MyPolicy」) ] 來啟用
"MyPolicy"
控制器的 CORS 原則。 - 停用 方法的
GetValues2
CORS。
如需測試上述程式碼的指示,請參閱 測試 CORS 。
CORS 原則選項
本節說明可在 CORS 原則中設定的各種選項:
AddPolicy 在 中 Startup.ConfigureServices
呼叫 。 對於某些選項,先閱讀 CORS 的運作方式 一節可能會很有説明。
設定允許的來源
AllowAnyOrigin:允許來自所有來源的 CORS 要求具有任何配置 (http
或 https
) 。 AllowAnyOrigin
不安全,因為 任何網站 都可以對應用程式提出跨原始來源要求。
注意
指定 AllowAnyOrigin
和 AllowCredentials
是不安全的組態,而且可能會導致跨網站偽造要求。 當應用程式使用這兩種方法設定時,CORS 服務會傳回無效 CORS 回應。
AllowAnyOrigin
會影響預檢要求和 Access-Control-Allow-Origin
標頭。 如需詳細資訊,請參閱 預檢要求 一節。
SetIsOriginAllowedToAllowWildcardSubdomains:將原則 IsOriginAllowed 的 屬性設定為函式,以允許原始來源在評估是否允許來源時比對已設定的萬用字元網域。
options.AddPolicy("MyAllowSubdomainPolicy",
policy =>
{
policy.WithOrigins("https://*.example.com")
.SetIsOriginAllowedToAllowWildcardSubdomains();
});
設定允許的 HTTP 方法
- 允許任何 HTTP 方法:
- 影響預檢要求和
Access-Control-Allow-Methods
標頭。 如需詳細資訊,請參閱 預檢要求 一節。
設定允許的要求標頭
若要允許在 CORS 要求中傳送特定標頭,稱為 作者要求標頭、呼叫 WithHeaders 並指定允許的標頭:
options.AddPolicy("MyAllowHeadersPolicy",
policy =>
{
// requires using Microsoft.Net.Http.Headers;
policy.WithOrigins("http://example.com")
.WithHeaders(HeaderNames.ContentType, "x-custom-header");
});
若要允許所有 作者要求標頭,請呼叫 AllowAnyHeader :
options.AddPolicy("MyAllowAllHeadersPolicy",
policy =>
{
policy.WithOrigins("https://*.example.com")
.AllowAnyHeader();
});
AllowAnyHeader
會影響預檢要求和 Access-Control-Request-Headers 標頭。 如需詳細資訊,請參閱 預檢要求 一節。
只有在 所 Access-Control-Request-Headers
傳送的標頭完全符合 中所述 WithHeaders
的標頭時,才可能符合 所 WithHeaders
指定之特定標頭的 CORS 中介軟體原則。
例如,請考慮設定的應用程式,如下所示:
app.UseCors(policy => policy.WithHeaders(HeaderNames.CacheControl));
CORS 中介軟體會拒絕具有下列要求標頭的預檢要求,因為 Content-Language
(HeaderNames.ContentLanguage) 未列于 : WithHeaders
Access-Control-Request-Headers: Cache-Control, Content-Language
應用程式會傳回 200 OK 回應,但不會將 CORS 標頭傳回。 因此,瀏覽器不會嘗試跨原始來源要求。
設定公開的回應標頭
根據預設,瀏覽器不會將所有回應標頭公開給應用程式。 如需詳細資訊,請參閱 W3C 跨原始來源資源分享 (術語) :簡單回應標頭。
預設可用的回應標頭如下:
Cache-Control
Content-Language
Content-Type
Expires
Last-Modified
Pragma
CORS 規格會呼叫這些標頭 的簡單回應標頭。 若要讓應用程式使用其他標頭,請呼叫 WithExposedHeaders :
options.AddPolicy("MyExposeResponseHeadersPolicy",
policy =>
{
policy.WithOrigins("https://*.example.com")
.WithExposedHeaders("x-custom-header");
});
跨原始來源要求中的認證
認證需要在 CORS 要求中特殊處理。 根據預設,瀏覽器不會傳送具有跨原始來源要求的認證。 認證包括 cookie s 和 HTTP 驗證配置。 若要傳送具有跨原始來源要求的認證,用戶端必須設定 XMLHttpRequest.withCredentials
為 true
。
直接使用 XMLHttpRequest
:
var xhr = new XMLHttpRequest();
xhr.open('get', 'https://www.example.com/api/test');
xhr.withCredentials = true;
使用 jQuery:
$.ajax({
type: 'get',
url: 'https://www.example.com/api/test',
xhrFields: {
withCredentials: true
}
});
使用 擷取 API:
fetch('https://www.example.com/api/test', {
credentials: 'include'
});
伺服器必須允許認證。 若要允許跨原始來源認證,請呼叫 AllowCredentials :
options.AddPolicy("MyMyAllowCredentialsPolicy",
policy =>
{
policy.WithOrigins("http://example.com")
.AllowCredentials();
});
HTTP 回應包含 Access-Control-Allow-Credentials
標頭,告知瀏覽器伺服器允許跨原始來源要求的認證。
如果瀏覽器傳送認證,但回應不包含有效的 Access-Control-Allow-Credentials
標頭,瀏覽器就不會向應用程式公開回應,而且跨原始來源要求會失敗。
允許跨原始來源認證是安全性風險。 另一個網域的網站可以代表使用者將登入使用者的認證傳送給應用程式,而不需要使用者知道。
CORS 規格也指出,如果 Access-Control-Allow-Credentials
標頭存在,將原點設定為 "*"
(所有原點) 無效。
預檢要求
對於某些 CORS 要求,瀏覽器會在提出實際要求之前傳送額外的 OPTIONS 要求。 此要求稱為 預檢要求。 如果 下列所有 條件都成立,瀏覽器可以略過預檢要求:
- 要求方法是 GET、HEAD 或 POST。
- 應用程式不會設定 、、
Accept-Language
Content-Language
、Content-Type
或Last-Event-ID
以外的Accept
要求標頭。 Content-Type
如果已設定標頭,則具有下列其中一個值:application/x-www-form-urlencoded
multipart/form-data
text/plain
用戶端要求所設定之要求標頭的規則會套用至應用程式在 物件上呼叫 setRequestHeader
所設定的 XMLHttpRequest
標頭。 CORS 規格會呼叫這些標頭 作者要求標頭。 此規則不適用於瀏覽器可以設定的標頭,例如 User-Agent
、 Host
或 Content-Length
。
以下是類似本檔 [測試 CORS] 區段中 [Put test]按鈕所提出預檢要求的範例回應。
General:
Request URL: https://cors3.azurewebsites.net/api/values/5
Request Method: OPTIONS
Status Code: 204 No Content
Response Headers:
Access-Control-Allow-Methods: PUT,DELETE,GET
Access-Control-Allow-Origin: https://cors1.azurewebsites.net
Server: Microsoft-IIS/10.0
Set-Cookie: ARRAffinity=8f8...8;Path=/;HttpOnly;Domain=cors1.azurewebsites.net
Vary: Origin
Request Headers:
Accept: */*
Accept-Encoding: gzip, deflate, br
Accept-Language: en-US,en;q=0.9
Access-Control-Request-Method: PUT
Connection: keep-alive
Host: cors3.azurewebsites.net
Origin: https://cors1.azurewebsites.net
Referer: https://cors1.azurewebsites.net/
Sec-Fetch-Dest: empty
Sec-Fetch-Mode: cors
Sec-Fetch-Site: cross-site
User-Agent: Mozilla/5.0
預檢要求會使用 HTTP OPTIONS 方法。 它可能包含下列標頭:
- Access-Control-Request-Method:將用於實際要求的 HTTP 方法。
- Access-Control-Request-Headers:應用程式在實際要求上設定的要求標頭清單。 如先前所述,這不包含瀏覽器設定的標頭,例如
User-Agent
。 - Access-Control-Allow-Methods
如果預檢要求遭到拒絕,應用程式會 200 OK
傳迴響應,但未設定 CORS 標頭。 因此,瀏覽器不會嘗試跨原始來源要求。 如需拒絕預檢要求的範例,請參閱本檔的 Test CORS 一節。
使用 F12 工具時,主控台應用程式會顯示類似下列其中一項的錯誤,視瀏覽器而定:
- Firefox:已封鎖跨原始來源要求:相同的原始來源原則不允許讀取位於
https://cors1.azurewebsites.net/api/TodoItems1/MyDelete2/5
的遠端資源。 (原因:CORS 要求未成功) 。 深入了解 - 以Chromium為基礎:CORS 原則已封鎖從來源 ' https://cors1.azurewebsites.net/api/TodoItems1/MyDelete2/5https://cors3.azurewebsites.net ' 擷取存取權:對預檢要求回應不會通過存取控制檢查:要求的資源上沒有 'Access-Control-Allow-Origin' 標頭存在。 如果不透明回應適合您的需求,請將要求的模式設定為 'no-cors' 以在停用 CORS 之下擷取資源。
若要允許特定標頭,請呼叫 WithHeaders :
options.AddPolicy("MyAllowHeadersPolicy",
policy =>
{
// requires using Microsoft.Net.Http.Headers;
policy.WithOrigins("http://example.com")
.WithHeaders(HeaderNames.ContentType, "x-custom-header");
});
若要允許所有 作者要求標頭,請呼叫 AllowAnyHeader :
options.AddPolicy("MyAllowAllHeadersPolicy",
policy =>
{
policy.WithOrigins("https://*.example.com")
.AllowAnyHeader();
});
瀏覽器在設定 Access-Control-Request-Headers
的方式中不一致。 如果下列其中一項:
- 標頭設定為以外的任何專案
"*"
- AllowAnyHeader 稱為:至少
Accept
包含 、Content-Type
和Origin
,以及您想要支援的任何自訂標頭。
自動預檢要求碼
套用 CORS 原則時:
- 在 中
Startup.Configure
呼叫 ,以全域方式呼叫app.UseCors
。 [EnableCors]
使用 屬性。
ASP.NET Core回應預檢 OPTIONS 要求。
目前使用 RequireCors
來啟用個別端點的 CORS 不支援 自動預檢要求。
本檔的 [測試 CORS ] 區段示範此行為。
[HttpOptions] 預檢要求的 屬性
使用適當的原則啟用 CORS 時,ASP.NET Core通常會自動回應 CORS 預檢要求。 在某些情況下,這可能不是這種情況。 例如,搭配 端點路由使用 CORS。
下列程式碼會使用 [HttpOptions] 屬性來建立 OPTIONS 要求的端點:
[Route("api/[controller]")]
[ApiController]
public class TodoItems2Controller : ControllerBase
{
// OPTIONS: api/TodoItems2/5
[HttpOptions("{id}")]
public IActionResult PreflightRoute(int id)
{
return NoContent();
}
// OPTIONS: api/TodoItems2
[HttpOptions]
public IActionResult PreflightRoute()
{
return NoContent();
}
[HttpPut("{id}")]
public IActionResult PutTodoItem(int id)
{
if (id < 1)
{
return BadRequest();
}
return ControllerContext.MyDisplayRouteInfo(id);
}
如需測試上述程式碼的指示,請參閱 使用端點路由測試 CORS 和 [HttpOptions ]。
設定預檢到期時間
Access-Control-Max-Age
標頭會指定快取預檢要求回應的時間長度。 若要設定此標頭,請呼叫 SetPreflightMaxAge :
options.AddPolicy("MySetPreflightExpirationPolicy",
policy =>
{
policy.WithOrigins("http://example.com")
.SetPreflightMaxAge(TimeSpan.FromSeconds(2520));
});
CORS 的運作方式
本節說明在 HTTP 訊息層級的 CORS 要求中會發生什麼情況。
- CORS 不是 安全性功能。 CORS 是 W3C 標準,可讓伺服器放寬相同的原始來源原則。
- 例如,惡意執行者可能會針對您的網站使用 跨網站腳本 (XSS) ,並對其已啟用 CORS 的網站執行跨網站要求來竊取資訊。
- 藉由允許 CORS,API 並不更安全。
- 用戶端 (瀏覽器) 強制執行 CORS。 伺服器會執行要求並傳迴響應,它是傳回錯誤的用戶端,並封鎖回應。 例如,下列任何工具都會顯示伺服器回應:
- Fiddler
- Postman
- .NET HttpClient
- 在網址列中輸入 URL,以使用網頁瀏覽器。
- 用戶端 (瀏覽器) 強制執行 CORS。 伺服器會執行要求並傳迴響應,它是傳回錯誤的用戶端,並封鎖回應。 例如,下列任何工具都會顯示伺服器回應:
- 這是讓伺服器能夠允許瀏覽器執行跨原始來源 XHR 或 擷取 API 要求的方式,否則會遭到禁止。
- 沒有 CORS 的瀏覽器無法執行跨原始來源要求。 在 CORS 之前,JS ONP是用來規避這項限制。 JSONP 不會使用 XHR,它會使用
<script>
標記來接收回應。 允許跨原始來源載入腳本。
- 沒有 CORS 的瀏覽器無法執行跨原始來源要求。 在 CORS 之前,JS ONP是用來規避這項限制。 JSONP 不會使用 XHR,它會使用
CORS 規格引進了數個新的 HTTP 標頭,可啟用跨原始來源要求。 如果瀏覽器支援 CORS,它會針對跨原始來源要求自動設定這些標頭。 啟用 CORS 不需要自訂 JavaScript 程式碼。
以下是從 [ 值 ] 測試按鈕到 https://cors1.azurewebsites.net/api/values
的跨原始來源要求範例。 標頭 Origin
:
- 提供提出要求之網站的網域。
- 為必要專案,且必須與主機不同。
一般標頭
Request URL: https://cors1.azurewebsites.net/api/values
Request Method: GET
Status Code: 200 OK
回應標頭
Content-Encoding: gzip
Content-Type: text/plain; charset=utf-8
Server: Microsoft-IIS/10.0
Set-Cookie: ARRAffinity=8f...;Path=/;HttpOnly;Domain=cors1.azurewebsites.net
Transfer-Encoding: chunked
Vary: Accept-Encoding
X-Powered-By: ASP.NET
要求標頭
Accept: */*
Accept-Encoding: gzip, deflate, br
Accept-Language: en-US,en;q=0.9
Connection: keep-alive
Host: cors1.azurewebsites.net
Origin: https://cors3.azurewebsites.net
Referer: https://cors3.azurewebsites.net/
Sec-Fetch-Dest: empty
Sec-Fetch-Mode: cors
Sec-Fetch-Site: cross-site
User-Agent: Mozilla/5.0 ...
在 OPTIONS
要求中,伺服器會在回應中設定Response 標頭Access-Control-Allow-Origin: {allowed origin}
。 例如,已部署 的範例Delete [EnableCors] 按鈕 OPTIONS
要求包含下列標頭:
一般標頭
Request URL: https://cors3.azurewebsites.net/api/TodoItems2/MyDelete2/5
Request Method: OPTIONS
Status Code: 204 No Content
回應標頭
Access-Control-Allow-Headers: Content-Type,x-custom-header
Access-Control-Allow-Methods: PUT,DELETE,GET,OPTIONS
Access-Control-Allow-Origin: https://cors1.azurewebsites.net
Server: Microsoft-IIS/10.0
Set-Cookie: ARRAffinity=8f...;Path=/;HttpOnly;Domain=cors3.azurewebsites.net
Vary: Origin
X-Powered-By: ASP.NET
要求標頭
Accept: */*
Accept-Encoding: gzip, deflate, br
Accept-Language: en-US,en;q=0.9
Access-Control-Request-Headers: content-type
Access-Control-Request-Method: DELETE
Connection: keep-alive
Host: cors3.azurewebsites.net
Origin: https://cors1.azurewebsites.net
Referer: https://cors1.azurewebsites.net/test?number=2
Sec-Fetch-Dest: empty
Sec-Fetch-Mode: cors
Sec-Fetch-Site: cross-site
User-Agent: Mozilla/5.0
在上述 Response 標頭中,伺服器會在回應中設定 Access-Control-Allow-Origin 標頭。 https://cors1.azurewebsites.net
此標頭的值符合 Origin
來自要求的標頭。
如果 AllowAnyOrigin 呼叫 , Access-Control-Allow-Origin: *
則會傳回萬用字元值。 AllowAnyOrigin
允許任何來源。
如果回應不包含 Access-Control-Allow-Origin
標頭,跨原始來源要求就會失敗。 具體而言,瀏覽器不允許要求。 即使伺服器傳回成功的回應,瀏覽器也不會將回應提供給用戶端應用程式。
顯示 OPTIONS 要求
根據預設,Chrome 和 Edge 瀏覽器不會在 F12 工具的網路索引標籤上顯示 OPTIONS 要求。 若要在這些瀏覽器中顯示 OPTIONS 要求:
chrome://flags/#out-of-blink-cors
或edge://flags/#out-of-blink-cors
- 停用 旗標。
- 重新 啟動。
Firefox 預設會顯示 OPTIONS 要求。
IIS 中的 CORS
部署至 IIS 時,如果伺服器未設定為允許匿名存取,CORS 必須在 Windows 驗證之前執行。 若要支援此案例,必須為應用程式安裝並設定 IIS CORS 模組 。
測試 CORS
範例下載具有測試 CORS 的程式碼。 請參閱如何下載。 此範例是已新增 Pages 的 Razor API 專案:
public class StartupTest2
{
public void ConfigureServices(IServiceCollection services)
{
services.AddCors(options =>
{
options.AddPolicy(name: "MyPolicy",
policy =>
{
policy.WithOrigins("http://example.com",
"http://www.contoso.com",
"https://cors1.azurewebsites.net",
"https://cors3.azurewebsites.net",
"https://localhost:44398",
"https://localhost:5001")
.WithMethods("PUT", "DELETE", "GET");
});
});
services.AddControllers();
services.AddRazorPages();
}
public void Configure(IApplicationBuilder app)
{
app.UseHttpsRedirection();
app.UseStaticFiles();
app.UseRouting();
app.UseCors();
app.UseAuthorization();
app.UseEndpoints(endpoints =>
{
endpoints.MapControllers();
endpoints.MapRazorPages();
});
}
}
警告
WithOrigins("https://localhost:<port>");
應該只用于測試類似 下載範例程式碼的範例應用程式。
下列 ValuesController
提供用於測試的端點:
[EnableCors("MyPolicy")]
[Route("api/[controller]")]
[ApiController]
public class ValuesController : ControllerBase
{
// GET api/values
[HttpGet]
public IActionResult Get() =>
ControllerContext.MyDisplayRouteInfo();
// GET api/values/5
[HttpGet("{id}")]
public IActionResult Get(int id) =>
ControllerContext.MyDisplayRouteInfo(id);
// PUT api/values/5
[HttpPut("{id}")]
public IActionResult Put(int id) =>
ControllerContext.MyDisplayRouteInfo(id);
// GET: api/values/GetValues2
[DisableCors]
[HttpGet("{action}")]
public IActionResult GetValues2() =>
ControllerContext.MyDisplayRouteInfo();
}
MyDisplayRouteInfo 是由 Rick.Docs.Samples.RouteInfo NuGet 套件提供,並顯示路由資訊。
使用下列其中一種方法來測試上述範例程式碼:
- 在 使用已部署的範例應用程式 https://cors3.azurewebsites.net/ 。 不需要下載範例。
- 使用 的預設 URL
https://localhost:5001
執行範例dotnet run
。 - 從 Visual Studio 執行範例,並將埠設定為 44398 作為 URL
https://localhost:44398
。
搭配 F12 工具使用瀏覽器:
選取 [ 值 ] 按鈕,然後檢閱 [ 網路 ] 索引標籤中的標頭。
選取 PUT 測試 按鈕。 如需顯示 OPTIONS 要求的指示,請參閱 顯示 OPTIONS 要求。 PUT 測試會建立兩個要求:OPTIONS 預檢要求和 PUT 要求。
GetValues2 [DisableCors]
選取按鈕以觸發失敗的 CORS 要求。 如檔中所述,回應會傳回 200 次成功,但不會提出 CORS 要求。 選取 [主控台] 索引卷 標以查看 CORS 錯誤。 視瀏覽器而定,會顯示類似下列的錯誤:CORS 原則已封鎖從
'https://cors1.azurewebsites.net/api/values/GetValues2'
來源'https://cors3.azurewebsites.net'
擷取的存取:要求的資源上沒有 'Access-Control-Allow-Origin' 標頭。 如果不透明回應適合您的需求,請將要求的模式設定為 'no-cors' 以在停用 CORS 之下擷取資源。
啟用 CORS 的端點可以使用 curl、 Fiddler或 Postman等工具進行測試。 使用工具時,標頭所 Origin
指定的要求來源必須與接收要求的主機不同。 如果要求不是以標頭的值 Origin
為基礎的跨原始來源:
- 不需要 CORS 中介軟體來處理要求。
- 回應中不會傳回 CORS 標頭。
下列命令會使用 curl
發出 OPTIONS 要求,其中包含資訊:
curl -X OPTIONS https://cors3.azurewebsites.net/api/TodoItems2/5 -i
使用端點路由和 [HttpOptions] 測試 CORS
目前使用 RequireCors
來啟用個別端點的 CORS不支援自動預檢要求。 請考慮使用 端點路由來啟用 CORS的下列程式碼:
public class StartupEndPointBugTest
{
readonly string MyPolicy = "_myPolicy";
// .WithHeaders(HeaderNames.ContentType, "x-custom-header")
// forces browsers to require a preflight request with GET
public void ConfigureServices(IServiceCollection services)
{
services.AddCors(options =>
{
options.AddPolicy(name: MyPolicy,
policy =>
{
policy.WithOrigins("http://example.com",
"http://www.contoso.com",
"https://cors1.azurewebsites.net",
"https://cors3.azurewebsites.net",
"https://localhost:44398",
"https://localhost:5001")
.WithHeaders(HeaderNames.ContentType, "x-custom-header")
.WithMethods("PUT", "DELETE", "GET", "OPTIONS");
});
});
services.AddControllers();
services.AddRazorPages();
}
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
app.UseHttpsRedirection();
app.UseStaticFiles();
app.UseRouting();
app.UseCors();
app.UseAuthorization();
app.UseEndpoints(endpoints =>
{
endpoints.MapControllers().RequireCors(MyPolicy);
endpoints.MapRazorPages();
});
}
}
下列 TodoItems1Controller
提供用於測試的端點:
[Route("api/[controller]")]
[ApiController]
public class TodoItems1Controller : ControllerBase
{
// PUT: api/TodoItems1/5
[HttpPut("{id}")]
public IActionResult PutTodoItem(int id)
{
if (id < 1)
{
return Content($"ID = {id}");
}
return ControllerContext.MyDisplayRouteInfo(id);
}
// Delete: api/TodoItems1/5
[HttpDelete("{id}")]
public IActionResult MyDelete(int id) =>
ControllerContext.MyDisplayRouteInfo(id);
// GET: api/TodoItems1
[HttpGet]
public IActionResult GetTodoItems() =>
ControllerContext.MyDisplayRouteInfo();
[EnableCors]
[HttpGet("{action}")]
public IActionResult GetTodoItems2() =>
ControllerContext.MyDisplayRouteInfo();
// Delete: api/TodoItems1/MyDelete2/5
[EnableCors]
[HttpDelete("{action}/{id}")]
public IActionResult MyDelete2(int id) =>
ControllerContext.MyDisplayRouteInfo(id);
}
刪除 [EnableCors]和GET [EnableCors]按鈕會成功,因為端點具有 [EnableCors]
並回應預檢要求。 其他端點會失敗。 GET按鈕失敗,因為JavaScript會傳送:
headers: {
"Content-Type": "x-custom-header"
},
下列 TodoItems2Controller
提供類似的端點,但包含回應 OPTIONS 要求的明確程式碼:
[Route("api/[controller]")]
[ApiController]
public class TodoItems2Controller : ControllerBase
{
// OPTIONS: api/TodoItems2/5
[HttpOptions("{id}")]
public IActionResult PreflightRoute(int id)
{
return NoContent();
}
// OPTIONS: api/TodoItems2
[HttpOptions]
public IActionResult PreflightRoute()
{
return NoContent();
}
[HttpPut("{id}")]
public IActionResult PutTodoItem(int id)
{
if (id < 1)
{
return BadRequest();
}
return ControllerContext.MyDisplayRouteInfo(id);
}
// [EnableCors] // Not needed as OPTIONS path provided
[HttpDelete("{id}")]
public IActionResult MyDelete(int id) =>
ControllerContext.MyDisplayRouteInfo(id);
[EnableCors] // Rquired for this path
[HttpGet]
public IActionResult GetTodoItems() =>
ControllerContext.MyDisplayRouteInfo();
[HttpGet("{action}")]
public IActionResult GetTodoItems2() =>
ControllerContext.MyDisplayRouteInfo();
[EnableCors] // Rquired for this path
[HttpDelete("{action}/{id}")]
public IActionResult MyDelete2(int id) =>
ControllerContext.MyDisplayRouteInfo(id);
}
從已部署範例 的測試頁面測試 上述程式碼。 在 [ 控制器 ] 下拉式清單中,選取 [ 預檢] ,然後選取 [ 設定控制器]。 所有對端點的 TodoItems2Controller
CORS 呼叫都成功。