前言
网上有很多跨域的文章,但是要么信息过时,要么不全,所以记录一下目前最新的情况,2022.07
跨域有哪些
跨域是什么不用多说了吧,我们看看各种跨域请求有啥问题
- ajax跨域,包括get、post、自定义header等情况
- ajax跨域,携带凭证,一般是cookie
- iframe跨域
- 跨域,安全问题
- 其他跨域方法
- iframe跨域通信
说明
配置了3个域名,并且配置了3个域名的https
cors-test1和cors-test2为同级
cors-test3.yyyy.com.cn和 cors-test1、cors-test2 分别的顶级域名
http://cors-test1.xxxx.com.cn
http://cors-test2.xxxx.com.cn
http://cors-test3.yyyy.com.cn
服务器文件:
/test-cors.html 测试前端页面
/test.php 测试后端页面
浏览器版本:
别的浏览器没测试,目前看chrome应该是最严格的,所以可以按照chrome来
chrome 102.0.5005.115
ajax跨域,包括get、post、自定义header等情况
这里发送请求时,不会携带cookie,但是可以有请求参数、自定义头等
发送简单请求
简单来说就是get,post请求,并且只能包含下面的header:
Accept
Accept-Language
Content-Language
Content-Type(需要注意额外的限制)
Content-Type 的值仅限于下列三者之一:
text/plain
multipart/form-data
application/x-www-form-urlencoded
发送个简单的请求,错误示例:
由cors-test1向cors-test2发送请求
fetch('http://cors-test2.xxxx.com.cn/test.php',{
}).then(response => response.text()).then(res =>{
console.log(res)
})
可以看到会得到一个cors错误,鼠标移上去会有详细的错误提示,没法截图,这里会提示:MissingAllowOriginHeader,就是响应头部缺少
Access-Control-Allow-Origin
发送个简单的请求,正确示例:
所以我们修改一下test.php,增加header
header("Access-Control-Allow-Origin: *");
echo 'ok';
可以看到我们在服务端增加了一个headerAccess-Control-Allow-Origin就可以实现跨域请求,是不是很简单?但是实际我们还有很多别的情况
发送非简单请:
非简单请求,就是除了简单请求之外的,一般是有自定义header情况
fetch('http://cors-test2.xxxx.com.cn/test.php',{
headers:{
token:'bbkjsi232',//自定义header
}
}).then(response => response.text()).then(res =>{
console.log(res)
})
可以看到浏览器会发送2个请求,第一个请求叫预检查请求,这个请求是浏览器自动发送的,请求类型为OPTIONS,预检查就是我先发送一个检查请求,服务端判断一下要不要我发送真正的请求
这时我们需要别的几个头了
Access-Control-Allow-Origin //允许的源,不能为通配符*了
Access-Control-Allow-Headers //允许的头
Access-Control-Allow-Methods //允许的请求方法,post,get等
Access-Control-Max-Age //缓存时间,这个时间内,浏览器不会再发送预检查,chrome最大时间为600秒
发送非简单请求,正确示例:
这时服务端的代码像这样的
//如果是options请求,只返回header
if($_SERVER['REQUEST_METHOD'] == 'OPTIONS'){
header("Access-Control-Allow-Origin: http://cors-test1.xxxx.com.cn");
header('Access-Control-Allow-Headers: token');
header('Access-Control-Allow-Methods: POST,GET');
header('Access-Control-Max-Age: 10');
exit;
}
header("Access-Control-Allow-Origin: *");
echo 'ok';
预检查请求
响应ok
ajax跨域,携带凭证,一般是cookie
凭证包括 HTTP cookies 和 HTTP 认证信息,但一般我们用cookie
发送请求,携带cookie:
fetch:credentials: "include"
xhr:withCredentials :true
fetch('http://cors-test2.xxxx.com.cn/test.php',{
mode:'cors',
credentials: "include",
}).then(response => response.text()).then(res =>{
console.log(res)
})
axios.get('http://cors-test2.xxxx.com.cn/test.php',{
withCredentials :true
})
目前header只返了Access-Control-Allow-Origin 并且为*,是不支持的
发送请求,携带cookie,正确示例:
这是需要2个响应头部
Access-Control-Allow-Credentials:true //允许凭证
Access-Control-Allow-Origin:http://cors-test1.xxxx.com.cn //origin必须指定名字,不能是通配符
可以看到没报错了,也返回数据
看看我们的请求
可以看到已经携带了cookie了,那么是谁的cookie呢?
可以看到实际发送的是目标网址的cookie,而不是自己的cookie
发送请求,携带cookie,跨顶级域名:
我们再来看看跨顶级域名,给cors-test3.yyyy.com.cn域名发送看看
fetch('http://cors-test3.yyyy.com.cn/test.php',{
mode:'cors',
credentials: "include",
}).then(response => response.text()).then(res =>{
console.log(res)
})
cookie情况
我们的cookie,cors-test3是有值的username=test3,按照刚才的情况,应该会发送username=test3
看看我们的请求
cookie并没有发送,并且服务端设置cookie也报错
这是就需要cookie的另一个属性,SameSite
SameSite 接受下面三个值:
Lax
Cookies 允许与顶级导航一起发送,并将与第三方网站发起的 GET 请求一起发送。这是浏览器中的默认值。
Strict
Cookies 只会在第一方上下文中发送,不会与第三方网站发起的请求一起发送。
None
Cookie 将在所有上下文中发送,即允许跨站发送。
以前 None 是默认值,但最近的浏览器版本将 Lax 作为默认值,以便对某些类型的跨站请求伪造(CSRF)攻击具有相当强的防御能力。
SameSite=None 需要 Secure ,如果设置了Secure,则需要站点为https
接下来我们设置一下cookie,访问地址 https://cors-test3.yyyy.com.cn/test-cors.html 注意https
//设置一个cookie,username-none
document.cookie = 'username-none=test3-none;SameSite=None;Secure'
再发起请求,访问地址:http://cors-test1.xxxx.com.cn/test-cors.html,站点为http
注意目标地址为https
fetch('https://cors-test3.yyyy.com.cn/test.php',{
mode:'cors',
credentials: "include",
}).then(response => response.text()).then(res =>{
console.log(res)
})
可以看到发送请求的时候携带了username-none ,并没有携带 username
跨顶级域名,只能发送SameSite=None的cookie,这需要目标站点为https
iframe跨域
iframe跨域一般就是嵌套别的页面,这里也是跨域cookie问题
先看看同顶级域名
<iframe src="http://cors-test2.xxxx.com.cn/test.php"></iframe>
可以看到请求发送了cookie,响应设置cookie也成功了
跨顶级域名,http嵌套
<iframe src="http://cors-test3.yyyy.com.cn/test.php"></iframe>
发送并没有cookie,并且设置cookie也有问题
跨顶级域名,https嵌套
可以看到,只能发送none的cookie,服务端设置cookie也必须要是none和Secure
跨域,安全问题
跨域安全问题实际上就是csrf攻击,csrf就是如果你现在登录了淘宝,同时新标签页打开了另外一个网站,这时另外一个网站通过ajax发起请求给淘宝, 不就是我们现在所讲的跨域情况吗?如果这时候浏览器没有限制自动发送了cookie给淘宝,而淘宝也是用cookie作为认证的话,那么不是会遭到破坏请求,例如拉取购物车等
iframe框问题也是一样的,如果另外一个网站用iframe框嵌套淘宝的页面,如果携带了cookie,那么你可能认为另外一个站点就是淘宝了,有可能让你乱操作
所以现在浏览器默认cookie都SameSite=Lax, None并不安全
另外现在都应该使用token了吧,也就是你自己负责把token记录下来,可以存到内存变量或者localStorage,存cookie并不是那么安全,因为浏览器会在一些情况自动发送
其他跨域方法
1.JSONP
这个原理自己网上查找吧
2.代理
也就是让自己的服务端去代理请求
iframe跨域通信
刚才讲的都是前端和服务端的通信,现在前端嵌套的iframe需要互相通信
当然也需要对iframe的内容能够修改才行
1.postMessage
网上查
2.websocket
2个站点都链接同一个服务器,相当于服务端做了一个实时中转
3.location.hash
就是设置iframe的链接的hash值,通过监听hash变化
总结
服务端响应头:
Access-Control-Allow-Origin //允许的源,不能为通配符*了
Access-Control-Allow-Headers //允许的头
Access-Control-Allow-Methods //允许的请求方法,post,get等
Access-Control-Max-Age //缓存时间,这个时间内,浏览器不会再发送预检查,chrome最大时间为600秒
预检查请求
对于非简单请求,浏览器会自动发送一个OPTIONS请求,验证是否运行跨域
发送请求是携带凭证
fetch:credentials: "include"
xhr:withCredentials :true
cookie SameSite属性
SameSite 接受下面三个值:
Lax
Cookies 允许与顶级导航一起发送,并将与第三方网站发起的 GET 请求一起发送。这是浏览器中的默认值。
Strict
Cookies 只会在第一方上下文中发送,不会与第三方网站发起的请求一起发送。
None
Cookie 将在所有上下文中发送,即允许跨站发送。 //需要https
参考
cors说明 developer.mozilla.org/zh-CN/docs/…
http header developer.mozilla.org/zh-CN/docs/…
fetch标准 fetch.spec.whatwg.org/
cookie各字段说明 developer.mozilla.org/zh-CN/docs/…
另外如果觉得不错,可以点个赞再走吧