Skip to content

16. CORS 跨域

16.1 什么是跨域

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

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

16.2 有跨域行为示例

当前页面 url 被请求页面 url 是否跨域 原因
http://www.furion.net/ http://www.furion.net/index.html 同源(协议、域名、端口号相同)
http://www.furion.net/ https://www.furion.net/index.html 跨域 协议不同(http/https)
http://www.furion.net/ http://www.baidu.com/ 跨域 主域名不同(furion.net/baidu.com)
http://www.furion.net/ http://docs.furion.net/ 跨域 子域名不同(www/docs)
http://www.furion.net:8080/ http://www.furion.net: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", "https://furion.net"]  
  }  
}  

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 307 状态码(Temporary Redirect)

在 ASP.NET Core 中处理跨域(CORS)问题时,如果遇到 307 状态码(Temporary Redirect),这通常是因为浏览器接收到服务器返回的重定向响应,并且在预检请求(OPTIONS)阶段或实际请求(POST, PUT 等)阶段由于 CORS 策略没有正确配置而导致的。

这时需要将所有可能的 Origins(域名/IP) 添加到 CORS 配置中,如:

{  
  "CorsAccessorSettings": {  
    "WithOrigins": [  
      "http://localhost:4200",  
      "https://furion.net",  
      "http://www.chinadot.net"  
    ] // 所有可能的域名/IP(含端口)  
  }  
}  

16.12 反馈与建议

与我们交流给 Furion 提 Issue


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