在 Sisk 中启用 CORS(跨域资源共享)
本页由英文自动翻译。 阅读原文
Sisk 提供了一个工具,可在公开服务时处理跨域资源共享 (CORS)。该功能并非 HTTP 协议的一部分,而是由 W3C 定义的浏览器特性。此安全机制阻止网页向提供该网页的域之外的域发起请求。服务提供者可以允许特定域或单个域访问其资源。
Same Origin #
要将资源标识为“同源”,请求必须在其请求中包含 Origin 头部:
GET /api/users HTTP/1.1
Host: example.com
Origin: http://example.com
...远程服务器必须在响应中返回一个与请求的 Origin 值相同的 Access-Control-Allow-Origin 头部:
HTTP/1.1 200 OK
Access-Control-Allow-Origin: http://example.com
...此验证是显式的:主机、端口和协议必须与请求的完全一致。请查看下面的示例:
- 服务器返回其
Access-Control-Allow-Origin为https://example.com:https://example.net- 域名不同。http://example.com- 协议不同。http://example.com:5555- 端口不同。https://www.example.com- 主机不同。
在规范中,两种头部仅允许使用语法形式,无论是请求还是响应。URL 路径会被忽略。默认端口(HTTP 为 80,HTTPS 为 443)也会被省略。
Origin: null
Origin: <scheme>://<hostname>
Origin: <scheme>://<hostname>:<port>Enabling CORS #
在原生环境中,你可以在 ListeningHost 中使用 CrossOriginResourceSharingHeaders 对象。
可以在初始化服务器时配置 CORS:
static async Task Main(string[] args)
{
using var app = HttpServer.CreateBuilder()
.UseCors(new CrossOriginResourceSharingHeaders(
allowOrigin: "http://example.com",
allowHeaders: ["Authorization"],
exposeHeaders: ["Content-Type"]))
.Build();
await app.StartAsync();
}上述代码会为所有响应发送以下头部:
HTTP/1.1 200 OK
Access-Control-Allow-Origin: http://example.com
Access-Control-Allow-Headers: Authorization
Access-Control-Expose-Headers: Content-Type这些头部必须在对 Web 客户端的所有响应中发送,包括错误和重定向。
你可能注意到 CrossOriginResourceSharingHeaders 类有两个相似的属性: AllowOrigin 和 AllowOrigins。请注意,一个是复数形式,另一个是单数形式。
- AllowOrigin 属性是静态的:仅会发送你指定的单一来源,适用于所有响应。
- AllowOrigins 属性是动态的:服务器会检查请求的来源是否在此列表中。如果匹配,则在该来源的响应中发送对应的头部。
当允许的来源取决于请求时(即使用 AllowOrigins 或下面描述的 AutoAllowOrigin 值),Sisk 还会发送 Vary: Origin 头部,以防缓存将响应复用于不同的来源。
Wildcards and automatic headers #
或者,你可以在响应的来源中使用通配符 (*) 来表示允许任何来源访问资源。但该值不适用于带有凭证(授权头部)的请求,此操作会导致错误。
可以通过显式列出允许的来源(使用 AllowOrigins 属性),或在 AllowOrigin 的值中使用 AutoAllowOrigin 常量来规避此问题。该魔法属性会将 Access-Control-Allow-Origin 头部设置为请求的 Origin 头部的相同值。
你还可以使用 AutoFromRequestMethod 和 AutoFromRequestHeaders 实现类似 AllowOrigin 的行为,自动根据发送的头部作出响应。
using var host = HttpServer.CreateBuilder()
.UseCors(new CrossOriginResourceSharingHeaders(
// 根据请求的 Origin 头部作出响应
allowOrigin: CrossOriginResourceSharingHeaders.AutoAllowOrigin,
// 根据 Access-Control-Request-Method 头部或请求方法作出响应
allowMethods: [CrossOriginResourceSharingHeaders.AutoFromRequestMethod],
// 根据 Access-Control-Request-Headers 头部或发送的头部作出响应
allowHeaders: [CrossOriginResourceSharingHeaders.AutoFromRequestHeaders],
exposeHeaders: [HttpKnownHeaderNames.ContentType, "X-Authenticated-Account-Id"],
allowCredentials: true))
.Build();Other Ways to Apply CORS #
如果你在使用服务提供者,可以覆盖配置文件中定义的值:
static async Task Main(string[] args)
{
using var app = HttpServer.CreateBuilder()
.UsePortableConfiguration(...)
.UseCors(cors => {
// 将覆盖配置文件中定义的来源
// 文件。
cors.AllowOrigin = "http://example.com";
})
.Build();
await app.StartAsync();
}Disabling CORS on Specific Routes #
UseCors 属性可用于路由及所有路由属性,并可通过以下示例禁用:
[RoutePrefix("api/widgets")]
public class WidgetController : Controller {
// GET /api/widgets/colors
[RouteGet("/colors", UseCors = false)]
public IEnumerable<string> GetWidgets() {
return new[] { "Green widget", "Red widget" };
}
}Replacing Values in the Response #
可以在路由操作中显式替换或移除头部值:
[RoutePrefix("api/widgets")]
public class WidgetController : Controller {
public IEnumerable<string> GetWidgets(HttpRequest request) {
// 移除 Access-Control-Allow-Credentials 头部
request.Context.OverrideHeaders.AccessControlAllowCredentials = string.Empty;
// 替换 Access-Control-Allow-Origin
request.Context.OverrideHeaders.AccessControlAllowOrigin = "https://contorso.com";
return new[] { "Green widget", "Red widget" };
}
}Preflight Requests #
预检请求是客户端在实际请求之前发送的OPTIONS 方法请求。
当 OPTIONS 方法的请求匹配某条路由的路径,但该路径没有显式配置 OPTIONS 路由时,Sisk 服务器会返回 200 OK 并附带相应的 CORS 头部,随后客户端即可继续发送实际请求。
- 如果路由使用 RouteMethod.Options 显式映射,例如使用
MapOptions,则该路由处理请求,而不是自动响应。 - 如果匹配路径的路由设置了
UseCors = false,仍会发送自动响应,但不包含 CORS 头部。
注意
在 Sisk 1.7 之前,自动预检响应始终携带 CORS 头部,即使路由设置了 UseCors = false。
Disabling CORS Globally #
这在技术上不可行。若不想使用 CORS,只需不要进行配置。