一、什么是跨域呢?
跨域的本质是浏览器基于同源策略的一种安全手段
同源策略(Sameoriginpolicy)
是浏览器最核心最基本的安全功能,是一种约定
所谓同源(即指在一个域)具有以下是三个共同点:
··协议相同(protocol)
··域名相同(host)
··端口号相同(port)
跨域
所以 非同源请求,也就是协议、域名或者端口其中一种不相同的时候,这个时候便产生了跨域
如果浏览器缺少了同源策略,很容易受到 XSS、CSRF等攻击
同源策略限制内容
··Cookie、LocalStorage、indexedDB等存储型内容
··DOM节点
··AJAX请求发送后、结果被浏览器拦截了
PS:跨域是浏览器的限制,当我们用抓包工具获取接口数据时,可以看到接口已经返回了数据,只是因为浏览器的限制,我们获取不到数据,用postman请求接口是可以请求到数据的。所以跨域并不能完全阻止CSRF,因为请求已经发出去了
允许跨域加载资源的标签
··<img src=XXX>
``<link href = XXX>
``<script src = XXX>
二、前端如何实现跨域请求?
1、常见跨域场景
不同域之间相互请求资源都算作跨域
但是:
第一,如果是协议和端口造成的跨域问题,“前台”无能为力
第二, 在跨域问题上,仅仅通过URL首部来识别而不会根据域名所对应的IP地址是否相同来判断
URL首部:可以理解为“协议、域名、端口必须匹配”
2、跨域有哪些方案呢
我先列举一下,不常用的有:
·document.domain+iframe:适用于主域名相同,子域名不同的跨域场景
·window.name+iframe:利用name值最长可以2M ,并用不同页面或者不同域名加载后以哦然存在的特性
·location.hash+iframe:适用通过C页面来实现A页面与B页面通信的场景
常用的有:
1、CORS
2、Nginx代理跨域
3、Node中间件代理跨域
4、WebSocket
5、postMessage
6、JSONP
接下来分别介绍一下各种常用方案
1、CORS
cors通信过程都是由浏览器自动完成,需要浏览器和服务器都支持,所以关键在于只要服务器支持就可以进行跨域通信,cors请求分为两类:简单请求与非简单请求
另外CORS请求默认不包含Cookie以及HTTP认证信息,如果需要包含Cookie,需要满足以下几个条件:
1.服务器指定了Access-Control-Allow-Credentials:true
2.开发者须在请求中打开WithCredentials属性:xhr.withCredentials = true
3.Access-Control-Allow-Origin不要设为星号,指定明确的与请求网页一致的域名,这样就不会把其他域名的Cookie上传
简单请求
需要同时满足两个条件,就属于简单请求:
··请求方法是:HEAD,GET,POST三者之一
··请求头的信息不超过以下几个字段:
·Accept
·Accept-Language
·Content-Language
·Last-Event-Id
·Content-Type:值为三者之一application/x-www/form/urlencoded\multipart/form-data\text/plain
需要这些条件是为了兼容表单,因为表单可以跨域
浏览器直接发出CORS请求,具体来说就是再头信息中增加Origin字段,表示请求来源来自哪个域(协议+域名+端口),服务器根据这个值决定是否同意请求,如果同意,返回的响应会多出响应头信息
在简单请求中服务器至少需要设置:Access-Contro-Allow-Origin字段
非简单请求
比如 put 或者 delete 请求,或 content-type 为 application/json,就是非简单请求
非简单CORS请求,正式请求前会发一次OPTIONS类型的查询请求,称为预检请求,询问服务器是否支持网页所在域名的请求,以及可以使用哪些头信息字段,只有收到肯定的答复才会正式发起XMLHttpRequest请求,否则报错
预检请求的方法是OPTIONS,他的头信息中有几个字段
··Origin:表示请求来自哪个域,这个字段是必须的
··Access-Control-Request-Method:列出Cors请求中会用到哪些HTTP方法,这个字段是必须的
··Access-Control-Request-Headers:指定CORS请求会额外发送的头信息字段,用,隔开
OPTIONS请求次数过多也会损耗性能,所以要尽量减少options请求,可以让服务器在请求返回头部添加Access-Control-Max-Age:Number //数字,单位:秒
表示预检请求的返回结果可以被缓存多久,在这个时间范围内再请求就不需要预检了(只对完全一样的URL有效)
2、Nginx代理跨域
配置一个代理服务器向服务器请求,在将数据返回给客户端,实质和CORS跨域原理一样,需要配置请求响应头Access-Contro-Allow-Origin等字段
server {
listen 81; server_name www.domain1.com;
location / {
proxy_pass http://xxxx1:8080; // 反向代理
proxy_cookie_domain www.xxxx1.com www.xxxx2.com; // 修改cookie里域名
index index.html index.htm;
// 当用webpack-dev-server等中间件代理接口访问nignx时,此时无浏览器参与,故没有同源限制,下面的跨域配置可不启用
add_header Access-Control-Allow-Origin http://www.xxxx2.com; // 当前端只跨域不带cookie时,可为*
add_header Access-Control-Allow-Credentials true;
}
}
3、Node中间件代理跨域
在VUE中vue.config.js中配置
module.export = {
...
devServer: {
proxy: {
[ process.env.VUE_APP_BASE_API ]: {
target: \'http://xxxx\',//代理跨域目标接口
ws: true,
changeOrigin: true,
pathRewrite: { [ \'^\' + process.env.VUE_APP_BASE_API ] : \'\'
}
}
}
}
}
Node+Express
const express = require(\'express\')
const proxy = require(\'http-proxy-middleware\')
const app = express()
app.use(\'/\', proxy({
// 代理跨域目标接口
target: \'http://xxxx:8080\',
changeOrigin: true,
// 修改响应头信息,实现跨域并允许带cookie
onProxyRes: function(proxyRes, req, res) {
res.header(\'Access-Control-Allow-Origin\', \'http://xxxx\')
res.header(\'Access-Control-Allow-Credentials\', \'true\')
},
// 修改响应信息中的cookie域名
cookieDomainRewrite: \'www.domain1.com\' // 可以为false,表示不修改
}));
app.listen(3000);
4、WebSocket
webSocket是HTML5标准中的一种通信协议,以ws://(非加密)和wss://(加密)作为协议前缀,该协议不实行同源政策,只要服务器支持就行
因为WebSocket请求头信息中有Origin字段,表示请求源来自哪个域服务器可以根据这个字段判断是否允许本次通信,如果在白名单内,就可以通信
5、PostMessage
PostMessage是Html5标准中的API,他可以给我们解决如下问题:
··页面和新打开的窗口间数据传递
··多窗口间数据传递
··页面与嵌套的iframe间数据传递
··以上三个场景间的跨域传递
postMessage接受两个参数,用法如下:
··参数一:发送的数据
··参数二:要发送给谁就写谁的地址(协议+域名+端口),也可以设置为*,表示任意窗口,为/表示与当前窗口同源的窗口
6、JSONP
原理就是通过添加一个<script>标签,向服务器请求JSON数据,这样不受同源政策限制。服务器收到请求后,将数据放在一个callback回调函数中传回来。比如axios
缺点是:只支持get请求且不安全,可能遇到XSS攻击
优点是:兼容性比CORS好
let script = document.createElement('script')
script.type = 'text/javascript'
script.src = 'http://juejin.com/xxx?callback=handleCallback'
document.body.appendChild(script)
function handleCallback(res){
console.log(res)
}
服务器返回并且立即执行
handleCallback({ code: 200, msg: 'success', data: [] })
三、跨域时COOKIE如何处理
指对第三方使用COOKIE的设置,在COOKIE信息中添加SameSite属性
Set-Cookie: widget_session=123456; SameSite=None; Secure
SameSite 有三个值:
strict:严格模式,完全禁止使用Cookie
lax:宽松模式,允许部分情况使用Cookie,跨域的都行,a标签跳转,link标签,GET提交的表单
none:任何情况下都会发送Cookie,但必须同时设置Secure属性,意思是需要安全上下文Cookie 只能通过https发送,否则无效
Chrome 80之前默认值是none,之后是lax
不过在最新的 Chrome91 版本中这个已经被移除了,所以在 91之前的版本依然可以使用
如果 Chrome 或 Edge 版本大于91小于94的话,可以通过Chromium支持的command-line flag
··右键 Chrome 或 Edge 浏览器,选择属性
··在目标(Target)属性末尾加上 :
--disable features=SameSiteByDefaultCookies,CookiesWithoutSameSiteMustBeSecure
并且官方说的到 94 版本会连 comman-line 也会移除
官方的说法是任由开发者控制这两个选项,容易被攻击