TL;DR
CORS (Cross-Origin Resource Sharing)
是一個支援安全跨源請求和資料傳輸的機制,用於在瀏覽器和伺服器之間進行跨源請求。
- 這是一個 W3C 標準,可讓伺服器放寬相同原始來源原則。
- 不是安全性功能,CORS 會放寬安全性。 允許 CORS 並不會增強 API 的安全性。CORS 的運作方式
- 允許伺服器明確允許某些跨原始來源要求,同時拒絕其他要求。
- 比舊版技術 (例如:JSONP) 更安全且更有彈性。
何謂相同原始來源
- 如果兩個 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
:不同的連接埠
- 這兩個 URL 具有相同的原始來源:
如何啟動 CORS
有三種方式可以啟用 CORS:
- 在中介軟體中,使用具名原則或預設原則。
- 使用端點路由。
- 使用 [EnableCors] 屬性。 搭配具名原則使用 [EnableCors] 屬性能夠以最精細的程度來控制對於支援 CORS 之端點的限制。
必須以正確的順序呼叫 UseCors。例如,在使用 UseResponseCaching 時,必須先呼叫 UseCors,再呼叫 UseResponseCaching。
具有具名原則和中介軟體的 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");
// policy.WithOrigins("http://example.com", "http://www.contoso.com")
// .AllowAnyHeader()
// .AllowAnyMethod(); ### policyBuilder 可以鏈結 ###
});
});
// 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 原則。 - 使回應快取中介軟體時,請先呼叫
UseCors
,再呼叫UseResponseCaching
。
使用端點路由時,CORS 中介軟體必須設定為在呼叫 UseRouting
和 UseEndpoints
之間執行。AddCors
方法呼叫會將 CORS 服務新增至應用程式的服務容器:
注意:指定的 URL 不得包含尾端斜線 (/)。 如果 URL 以 / 終止,則比較會傳回 false,而且不會傳回任何標頭。
- UseCors 和 UseStaticFiles 順序
一般而言,會先呼叫
UseStaticFiles
,再呼叫UseCors。
使用 JavaScript 來擷取跨網站靜態檔案的應用程式必須先呼叫UseCors
,再呼叫UseStaticFiles
。
具有預設原則和中介軟體的 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
使用端點路由時,可以使用一組 RequiredCors
擴充方法,逐個端點啟用 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
和控制器端點允許使用指定的原則進行跨原始來源要求。/echo2
和 Razor Pages 端點不允許跨原始來源要求,因為未指定預設原則。 如果 CORS 是由端點路由透過RequireCors
所啟用的,則[DisableCors]
屬性無法停用 CORS。
使用屬性啟用 CORS
使用[EnableCors]
屬性啟用 CORS,並將具名原則套用至只需要 CORS 的端點,這樣才能提供最精細的控制。
[EnableCors]
屬性提供全域套用 CORS 的替代方案。[EnableCors]
屬性會啟用所選端點的 CORS,而不是所有端點:
[EnableCors]
指定預設原則。[EnableCors("{Policy String}")]
指定具名原則。
[EnableCors] 屬性可以套用至:
- Razor Page 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。