跨域的几种方式
参考资料:https://www.bilibili.com/video/av31173487?from=search&seid=17845079103401026420
这里后端全部使用koa,写的只是为了方便理解,很多实际中不应该这样写。例子重复代码太多,但是直接粘贴就可以测试。
1.JSONP
1) 举个🌰,打开百度搜索
修改一下请求地址的查询参数cb
部分
代码如下:
1 |
|
最后打印出结果
2) 实现一个简单JSONP例子
1 |
|
koa部分:
1 | const koa = require('koa'); |
3)封装一个JSONP函数
写完以后感觉,这写的什么鬼,我大概是忘了想写什么。
1 | function jsonp(url, params, cb) { |
复制一下视频里老师写的
1 | function jsonp1({url,params,cb}) { |
4) JSONP的缺点
1.必须传回调函数名称
2.只支持get请求
3.不安全 XSS攻击
2.CORS(最常用,安全性高)
1)先写一个简单🌰,一个get的请求(代码都是基于前一个的基础上修改)
1 |
|
koa部分:
1 | const koa = require('koa'); |
运行代码,发送请求的时候可以看到浏览器, Access-Control-Allow-Origin
。表示后台没有允许域的访问。
同时也可以看到后台打印出来的结果
可以看出其实ajax请求已经发出,只是返回的时候被浏览器屏蔽。图中可以看到访问服务器的来源。
如何证明是返回被浏览器拦截:写一个简单的服务,一个get请求,里面操作数据库,操作成功了,结果返回给浏览器的时候失败。
解决方案:
我们添加了一个whiteList(白名单),并且判断如果来源在白名单内,就设置响应头。
1 | const koa = require('koa'); |
可以看到请求成功了
2)第二个🌰,自定义请求头
不修改koa代码的情况下,修改html。
1 |
|
请求发送后看到报错OPTIONS
请求挂了
问题原因:非简单请求,会先发送一个OPTIONS
预请求。
解决方案:
1 | const koa = require('koa'); |
然后又出现了新的报错Access-Control-Allow-Headers
,头部不被允许。
解决方案:设置允许的请求头部,多个头部使用逗号
分隔
1 | const koa = require('koa'); |
可以看到请求成功了
3)第三个🌰,发一个PUT请求
1 |
|
同时接收一个PUT请求
1 | const koa = require('koa'); |
然后出现了报错Access-Control-Allow-Methods
。请求方法不允许。
解决方案:
1 | const koa = require('koa'); |
可以看到请求成功了
了解一下OPTIONS
请求
在非简单请求的时候,会先发送一个预请求,主要用来判断是否允许跨域(我认为就是询问后台哪些头部,方法和来源是被允许的)。
在多次强制刷新页面的时候会发现,并不是每次请求的时候都会发送OPTIONS
请求,有时候会发送
有时不会发送
问题原因:浏览器会加上一个默认的时间,也可以通过Access-Control-Max-Age
设置
1 | const koa = require('koa'); |
4)第四个🌰,设置cookie
1 |
|
在后台把请求头打印出来看看,嗯!毛都没看到
问题原因:因为cookie
是不允许跨域的(需要浏览器和服务器同时允许)。
解决方案:我非要加上,设置 withCredentials
1 |
|
看到的报错Access-Control-Allow-Credentials
,
解决方案:添加头
1 | const koa = require('koa'); |
可以看到请求成功了
然而,还是没在请求头里看到cookie
,这个问题半天没查明白。猜测是只能带上服务器设置了的cookie,没添加的话就不行,先记着后面来补充!!!!!!!!!,后面来补充!!!!!!!!!,后面来补充!!!!!!!!!
5)设置响应头
在访问某些接口的时候,可能数据放在了响应头里。
1 | const koa = require('koa'); |
前端部分直接从响应头中取值
1 |
|
看到报错 ,不允许获得不安全的头
解决方案:我天真的以为这种问题,看起来应该是前端解决的,然鹅,还是要设置响应头Access-Control-Expose-Headers
1 | const koa = require('koa'); |
可以看到请求成功了
3.postMessage
1) 使用postMessage进行不同域页面之间的通信
首先新建两个页面:
第一个postMessage.html,这个页面iframe引入第二个页面的地址,并且向第二个页面推送。
1 |
|
和postMessage1.html,这个页面监听传入的消息
1 |
|
目录结构是这样的,我们使用puer(安装:npm install puer)启动本地服务器,让两个页面的端口号不同。
2)内嵌页面的回应
postMessage.html
1 |
|
postMessage1.html
1 |
|
可以看到页面输出结果
4.window.name
关于window.name
属性: name 值在不同的页面(甚至不同域名)加载后依旧存在(如果没修改则值不会变化),并且可以支持非常长的 name 值(2MB)。并且window.name
很方便。
1)第一个🌰,新建两个页面,并且让这两个页面不同域。name.html
启在端口号8000
,name1.html
启在端口号8001
,我在页面name.html去获取name1的数据。在加载页面name1.html
的时候,设置window.name='qinhanwen'
name.html
1 |
|
name1.html
1 |
|
然后看到报错,因为域不同,所以无法获取。
解决方案:新建一个name2.html
把iframe的src设置为这个页面,他与name.html
同域
name2.html 实际上这个name2.html什么都没做
1 |
|
name.html
1 |
|
可以看到获取成功了
5.location.hash
路径后的hash值可以用于通信
1)新建三个页面 hash.html
与hash1.html
同域,与hash2.html
不同域。
hash.html
1 |
|
hash2.html
1 |
|
可以看到打印出了 #qinhanwen
2)那么要怎么传递给值给父窗体
使用window.parent.hash
我们把父窗体的hash设置为#qinhanwen
hash2.html
1 |
|
然后看到报错,不同域的话,是不允许操作父窗体hash值的
解决方案:新增一个hash1.html
页面,在hash2.html
中添加一个iframe,iframse的 src指向与hash.html
同域的hash1.html
地址,而在hash1.html
设置父亲的父亲窗体的hash值,看🌰
hash1.html
1 |
|
hash2.html
1 |
|
可以看到hash被设置为了#qinhanwen
3)hash值是改变了,怎么取得呢
hash.html
1 |
|
6.document.domain
这个要先了解一级域名和二级域名,大概视频80分钟的时候开始说。
7.WebSocket
8.Nginx
大概视频90多分钟开始说。