跨域的几种实现方式(JSONP,Proxy,CORS)
同源策略
同源策略是一个重要的安全策略,它用于限制一个origin的文档或者它加载的脚本如何能与另一个源的资源进行交互。它能帮助阻隔恶意文档,减少可能被攻击的媒介。
同源的定义
如果两个 URL 的 protocol
、port
和 host
都相同的话,则这两个 URL
是同源。
源的更改
满足某些限制条件的情况下,页面是可以修改它的源。脚本可以将 document.domain 的值设置为其当前域或其当前域的父域。
例如:
假设 http://store.company.com/dir/other.html 文档中的一个脚本执行以下语句:
document.domain = "company.com";
然而company.com
不能设置为 othercompany.com
,因为它不是 company.com
的父域。
源的继承?
跨域方法
实现跨域请求有以下方法:JSONP,Proxy,CORS,Nginx,Socket...
JSONP:
JSONP实现原理:利用script标签不受同源策略限制,在页面动态添加script,script标签的src属性设成api地址,并将回调函数名以get方式告诉后端,后端将数据以参数的形式返回
JSONP的优缺点:只能发get请求、不安全、老式浏览器支持性好
Proxy:
通过代理服务器转发请求,实现跨域
CORS
老版浏览器不支持、支持所有谓词请求,推荐使用
带策略的CORS 和中间件
CORS中间件处理跨域请求。下面的代码允许指定的源能对整个应用进行跨域请求
案例:
CORS中间件处理跨域请求。下面的代码允许指定的源能对整个应用进行跨域请求
这个代码会把CORS策略通过CORS中间件应用到这个应用的所有终端(endpoints);即把跨域作用到整个应用
指定域名可以跨域请求:
public void ConfigureServices(IServiceCollection services)
{
string[] origins = new string[] { "http://*.fan.com", "https://*.fan.com", "http://*.fan.net", "https://*.fan.net" };
services.AddCors(option => option.AddPolicy("cors", policy => policy
.AllowAnyHeader()//允许所有的请求头
//.WithHeaders(HeaderNames.ContentType, "x-custom-header");//要允许一个CORS请求中指定的请求头,可以使用 WithHeaders 来指定
.AllowAnyMethod()//允许所有Method
.SetIsOriginAllowedToAllowWildcardSubdomains()//使可以匹配一个配置的带通配符的域名
.WithOrigins(origins) //指定来源域名
.AllowCredentials() //允许浏览器在跨域请求中发送证书
));
}
public void Configure(IApplicationBuilder app, IWebHostEnvironment env, IHostApplicationLifetime applicationLifetime, ILogger<Startup> logger , ElasticSearch elasticSearch)
{
app.UseCors("cors");//注意:1.UseCors必须在UseMvc之前被调用;2. URL末尾不能加/ ;这个url指的是 builder.WithOrigins(url)中的url
}
不做限制,允许所有域名跨域请求:
services.AddCors(option => option.AddPolicy("cors", policy => policy
.AllowAnyHeader()
.AllowAnyMethod()
.SetIsOriginAllowed(_ => true) //不做任何限制
.AllowCredentials()
)
);
使用[EnableCors]属性设置允许跨域
[EnableCors]属性提供了另一种方式设置跨域。即可以只设置选择的终端,而不是所有的终端.
这里不同于上面的那种方式,上面的方式是应用的所有终端都会被设置允许跨域;
使用[EnableCors]来指定默认的策略,而[EnableCors("{Policy String}")] 指定了特定的策略;
[DisableCors]属性可以禁止CORS;
[EnableCors("AnotherPolicy")]
[HttpGet]
public ActionResult<IEnumerable<string>> Get()
{
return new string[] { "green widget", "red widget" };
}
CORS 策略(Policy)的选项:
- 设置允许的访问源
- 设置允许的HTTP methods
- 设置允许的请求头(request header)
- 设置暴露的响应头(response header)
- 跨不同源请求的证书(Credentials)
- 设置过期时间
SetIsOriginAllowedToAllowWildcardSubdomains
:设置策略的 IsOriginAllowed 属性,使可以匹配一个配置的带通配符的域名
AllowAnyMethod
:设置允许的HTTP methods
WithHeaders
:要允许一个CORS请求中指定的请求头,可以使用 WithHeaders 来指定
AllowAnyHeader
:允许所有的请求头
withCredentials
:允许在跨域请求中发送证书
WithExposedHeaders
:暴露指定的响应头
注意:如果
Access-Control-Allow-Credentials
头部出现了,则意味着 设置为所有的源 (setting origin to " * ")会失效。
JSONP
过滤器:
public class JsonpFilter : IActionFilter
{
public void OnActionExecuting(ActionExecutingContext context)
{
// do something before the action executes
}
public void OnActionExecuted(ActionExecutedContext context)
{
// do something after the action executes
//if (context.HttpContext.Request.Headers.FirstOrDefault(x => x.Key == "X-Requested-With").Value == "XMLHttpRequest")
//{
// context.Result = new JsonpResult(new JsonResult(context.Exception), context.HttpContext.Request.Query["callback"]);
// return;
//}
if (context.Exception != null || context.Result == null || !(context.Result is JsonResult)) return;
if (context.HttpContext.Request.Method != "GET") return;
if (string.IsNullOrEmpty(context.HttpContext.Request.Query["callback"])) return;
context.Result = new JsonpResult(context.Result as JsonResult, context.HttpContext.Request.Query["callback"]);
}
public class JsonpResult : ActionResult
{
private static readonly string DefaultContentType = new MediaTypeHeaderValue("application/json")
{
CharSet = "utf-8"
}.ToString();
private readonly JsonResult result = null;
private readonly string callback;
public JsonpResult(JsonResult p, string callback)
{
result = p;
this.callback = callback;
}
public override Task ExecuteResultAsync(ActionContext context)
{
if (result == null)
{
throw new ArgumentNullException(nameof(result));
}
if (context == null)
{
throw new ArgumentNullException(nameof(context));
}
var services = context.HttpContext.RequestServices;
var Options = services.GetRequiredService<IOptions<MvcJsonOptions>>().Value;
var writerFactory = services.GetRequiredService<IHttpResponseStreamWriterFactory>();
var _charPool = new JsonArrayPool<char>(services.GetRequiredService<ArrayPool<char>>());
var response = context.HttpContext.Response;
ResponseContentTypeHelper.ResolveContentTypeAndEncoding(
result.ContentType,
response.ContentType,
DefaultContentType,
out var resolvedContentType,
out var resolvedContentTypeEncoding);
response.ContentType = resolvedContentType;
if (result.StatusCode != null)
{
response.StatusCode = result.StatusCode.Value;
}
var serializerSettings = result.SerializerSettings ?? Options.SerializerSettings;
using (var writer = writerFactory.CreateWriter(response.Body, resolvedContentTypeEncoding))
{
var jsonSerializer = JsonSerializer.Create(serializerSettings);
using (var jsonWriter = new JsonTextWriter(writer))
{
jsonWriter.ArrayPool = _charPool;
jsonWriter.CloseOutput = false;
jsonWriter.AutoCompleteOnClose = false;
jsonWriter.WriteRaw(callback + "(");
jsonSerializer.Serialize(jsonWriter, result.Value);
jsonWriter.WriteRaw(");");
}
}
return Task.CompletedTask;
}
}
}
参考:
https://www.cnblogs.com/Vincent-yuan/p/10801513.html
https://docs.microsoft.com/zh-cn/aspnet/core/security/cors