❤️ 关注 Furion 微信公众号有惊喜哦!
Skip to main content

16. CORS 跨域

16.1 什么是跨域

简单来说,当一个请求 url 的协议、域名、端口三者之间任意一个与当前页面 url 不同即为跨域。那为什么会出现跨域问题呢?

出于浏览器的同源策略限制。同源策略(Sameoriginpolicy)是一种约定,它是浏览器最核心也最基本的安全功能,如果缺少了同源策略,则浏览器的正常功能可能都会受到影响。可以说 Web 是构建在同源策略基础之上的,浏览器只是针对同源策略的一种实现。同源策略会阻止一个域的 javascript 脚本和另外一个域的内容进行交互。所谓同源(即指在同一个域)就是两个页面具有相同的协议(protocol),主机(host)和端口号(port)。

16.2 有跨域行为示例

当前页面 url被请求页面 url是否跨域原因
http://www.baiqian.ltd/http://www.baiqian.ltd/index.html同源(协议、域名、端口号相同)
http://www.baiqian.ltd/https://www.baiqian.ltd/index.html跨域协议不同(http/https)
http://www.baiqian.ltd/http://www.baidu.com/跨域主域名不同(baiqian.ltd/baidu.com)
http://furion.baiqian.ltd/http://fur.baiqian.ltd/跨域子域名不同(furion/fur)
http://www.baiqian.ltd:8080/http://www.baiqian.ltd:7001/跨域端口号不同(8080/7001)

16.3 什么是 CORS

跨源资源共享 (CORS) :

  • 是一种 W3C 标准,可让服务器放宽相同的源策略。
  • 不是一项安全功能,CORS 放宽 securityAPI 不能通过允许 CORS 来更安全。 有关详细信息,请参阅 CORS 工作原理
  • 允许服务器明确允许一些跨源请求,同时拒绝其他请求。
  • 比早期的技术(如 JSONP)更安全且更灵活。

16.4 如何使用

16.4.1 添加 CORS 服务

启用跨域 Cors 支持首先添加 CorsAccessor 服务,如:

using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;

namespace Furion.Web.Core
{
[AppStartup(700)]
public sealed class FurWebCoreStartup : AppStartup
{
public void ConfigureServices(IServiceCollection services)
{
services.AddCorsAccessor();

// ...
}

public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
//...

app.UseCorsAccessor();

// ...
}
}
}
特别注意

services.AddCorsAccessor(); 需在 services.AddControllers() 之前注册。

app.UseCorsAccessor(); 需在 app.UseRouting();app.UseAuthentication(); 之间注册。

16.4.2 配置允许跨域域名

小提醒

默认情况下,Furion 允许所有域名来源访问,也就是无需配置任何来源域名,另外前端也需要设置请求参数:withCredentials:false

如果需要指定特定域名,则添加以下配置即可:

{
"CorsAccessorSettings": {
"PolicyName": "自定义跨域策略名",
"WithOrigins": ["http://localhost:4200", "http://furion.baiqian.ltd"]
}
}

16.5 CorsAccessorSettings 配置

  • CorsAccessorSettings
    • PolicyName:跨域策略名,string 类型,必填,默认 App.Cors.Policy
    • WithOrigins:允许跨域的域名列表,string[] 类型,默认 *
    • WithHeaders:请求表头,没有配置则允许所有表头,string[] 类型
    • WithExposedHeaders:设置客户端可获取的响应标头,string[] 类型,默认 ["access-token", "x-access-token"]
      • 默认情况下,若后端输出特定的响应头 Key,那么需将该 Key 配置在数组中
    • WithMethods:设置跨域允许请求谓词,没有配置则允许所有,string[] 类型
    • AllowCredentials:是否允许跨域请求中的凭据,bool 类型,默认值 true
    • SetPreflightMaxAge:设置预检过期时间,int 类型,默认值 24小时
    • FixedClientToken:是否默认配置 WithExposedHeadersbool 类型,默认 true
    • SignalRSupport:是否启用 SignalR 跨域支持,bool 类型,默认 false

16.6 前端不能读取响应头注意事项

有时候,我们通过 ajax 或者 axios 第三方库无法读取响应头自定义信息,这时需要响应报文中公开特定 Header 才能放行,如:Access-Control-Expose-Headers: xxxxx,所以,需要添加以下配置:

appsettings.json
{
"CorsAccessorSettings": {
"WithExposedHeaders": ["access-token","x-access-token"]
}
}

需要获取哪个头,就在 WithExposedHeaders 数组中配置即可。如果使用 ajax 可以通过 xhr.getResponseHeader(key)xhr.getAllResponseHeaders() 获取配置的 key

特别情况下不能请求,可以考虑设置 withCredentials: false

16.7 使用 $.ajax 前端注意事项

使用 Jquery 前端请求可以参考以下配置:

$.ajax({
       url: "https://localhost:5001/api/system/getdata",
       type: "GET",
       xhrFields: {
           withCredentials: false // 如果是https请求,可以试试 true
       },
       crossDomain: true,
       success: function (res) {
           render(res);
       }
});
特别注意

在本地开发阶段,请求如果出现 Access to XMLHttpRequest...has been blocked by CORS policy: No 'Access-Control-Allow-Origin' header... 错误,请确保 ajaxurl 参数是正确的,通常错误的做法是:

  • 使用了 127.0.0.1 而不是 localhost 主机地址
  • 使用 http 而不是 https 主机协议
  • 使用了 5000 而不是 5001 主机端口

16.8 禁用跨域

有时候,我们希望某个方法不检查跨域请求,可以在 Action 中贴 [DisableCors] 特性即可。

16.9 SignalR 跨域问题

SignalR 实现跨域需要满足下面几个条件:

  • 允许特定的预期来源,允许任何来源是可行的,但不安全或不推荐使用
  • 必须允许使用 HTTP 方法 GETPOST
  • 为了使基于 cookie 的粘滞会话正常工作,必须允许使用凭据,即使未使用身份验证,也必须启用它们。

官方文档说明 https://docs.microsoft.com/zh-cn/aspnet/core/signalr/security?view=aspnetcore-6.0

Furion 4.1.4+ 版本已修正 SignalR 跨域问题,只需要启用 SignalRSupport 配置即可,如:

{
"CorsAccessorSettings": {
"SignalRSupport": true
}
}

16.10 静态资源跨域问题

有时候我们可能通过前端 XMLHttpRequest/Ajax/Fetch 方式加载静态资源,这时可能出现跨域问题,可以通过以下配置解决:

app.UseStaticFiles(new StaticFileOptions
{
OnPrepareResponse = (stf) =>
{
stf.Context.Response.Headers["Access-Control-Allow-Origin"] = "*";
stf.Context.Response.Headers["Access-Control-Allow-Headers"] = "*";
}
});
小知识

如果已经注册了 app.UseStaticFiles(),则只需要传递 new StaticFileOptions{ ... } 参数即可,避免多次注册 app.UseStaticFiles()

16.11 反馈与建议

与我们交流

给 Furion 提 Issue


了解更多

想了解更多 跨域请求 知识可查阅 ASP.NET Core - 启用跨域请求 章节。