JS笔记之跨域
跨域原因
跨域是浏览器上才有的,因为浏览器的安全机制同源策略,所以诞生了一个这玩意。
但实际上你在浏览器向服务器请求获取已经成功了,服务器也响应了,给了你想要的东西,但是因为同源策略
被浏览器给拦住了,不给你。
协议 域名 端口
这中间有一个不一样就会触发跨域问题
例如以下
-
http://www.zhouyumao.top:8080
->http://www.zhouyumao.top:80
原因端口不一样 -
http://www.zhouyumao.top
->http://www.blabla.com
原因域名不一样 -
http://www.zhouyumao.top
->https://www.zhouyumao.top
原因协议不一样 -
另外一种情况,同一个域名下的子域名不一样也会触发跨域如,
http://zh.zhouyumao.top
->http://www.zhouyumao.top
原因也是域名不一样
解决跨域
JSONP
- 通过script标签获取来解决,但是要需要和后端规定好相应的回调函数,后端需要对JSONP单独给他写个响应格式
- 只能解决
GET
请求问题,因为script标签只能发送GET
请求
所以JSONP并不是一种好的解决方案,这是很老的方案了
JSONP 客户端代码
服务端部分代码
CORS
CORS
是http1.1
的一种跨域解决方案,全称 Cross-Origin Resource Sharing,跨域资源共享
大体思路是:浏览器跨域请求服务器资源,需要获得服务器的允许
因为有的请求只是获取一些资源,有的请求却会更改服务器的数据,这就需要辨别
所以针对不同的请求,CORS就给出了三个交互模式分别是
- 简单请求
- 需要预检的请求
- 附带身份凭证的请求
三个模式从上到下,层层递进,请求的能做的事越来越多,也越来越严格
简单请求
当浏览器运行一段Ajax代码时,会先判断是哪一种请求,模式
简单请求的判断
请求必须同时满足以下条件
- 请求方法是以下一种
- get
- post
- head
- 请求头仅包含安全字段,常见安全字段
- Accept 表示客户端期望服务器返回的媒体格式
- Accept-Language 表示客户端期望服务器返回内容的语言
- Content-Language 表示服务响应给客户端body里面是什么语言
- Content-Type 表示服务器向客户端发送的响应头,代表媒体类型和编码格式是对Accept和Accept-Charset的回应
- content-Type 仅限以下三个值
- text/plain
- multipart/from-data
- application/x-www-from-urlencoded
- 请求中没有使用 ReadableStream对象
- 请求中的任意XMLHttpRequest对象均没有注册任何事件,可以使用XMLHttpRequest.upload()访问
例子
简单请求规范
当进行Ajax请求时,被浏览器判定跨域会发生以下情况
例如在http://www.zhouyumao.top/index.html
中发生了跨域请求 http://zh.zhouyumao.top/api/user
-
浏览器自动添加
Origin
字段请求头部分字段
最后一行
Origin
会告诉服务器那个域名在跨域 -
服务器响应头中应包含Access-Control-Allow-Origin
当服务器收到请求后,如果允许跨域,需要要在响应头中添加Access-Control-Allow-Origin字段
字段值有两种
- “*” 表示允许任何网站跨域,(并不推荐使用,因为如果后面请求附带身份认证的话就会不行,报错)
- 具体的源,表示值与请求头
Origin
的值一样 例如Access-Control-Allow-Origin : http://www.zhouyumao.top
服务器响应
服务端支持简单请求跨域(nodejs环境使用express搭建服务器插入一个中间件)
需要预检的请求
当浏览器认为某个请求是非简单请求时,就会走下面流程
- 浏览器发送预检请求,询问服务器是否允许
- 服务器允许
- 浏览器是发送真实请求
- 服务器完成响应
例如:http:www.zhouyumao.top/index.html
中有Ajax请求跨域了
当发送该请求时,浏览器就会发现这不是简单请求,headers
中添加了一个不属于安全字段的i
字段、Content-Type
字段值也不是限定的三个值之一,并且也增加了一个请求体body
浏览器与服务器交互流程如下
-
浏览器发送预检请求,询问服务器是否允许
客户端发送的预检请求(浏览器自动发送)
这是一个预检请求,询问服务器是否允许后续请求操作
预检请求有以下特征
- 请求方法为
OPTIONS
- 没有请求体
- 请求头包含
Origin
和简单请求一样Access-Control-Request-Method
后续真正的请求方法Access-Control-Request-Headers
后续真正的请求头会改动的内容
- 请求方法为
-
服务器允许
服务器收到
OPTIONS
预检请求后,就会检查是否允许,如果允许就会响应包含以下格式的消息服务器对预检请求,响应不带响应体,只是发送个响应头给浏览器
Access-Control-Allow-Methods
允许的请求方法Access-Control-Allow-Headers
允许改动的请求头Access-Control-Allow-Origin
和简单请求一样,允许的跨域源- Access-Control-Max-Age 告浏览器,下次同样的源,方法,请求头在36000秒内就不用再预检了。
-
浏览器发送真正的请求
-
服务器响应真正的请求
交互到这完成
服务端部分代码
附带身份请求
对于跨域请求,浏览器是默认不带Cookie的,如果没带Cookie一些验证就不能继续
不过也可以自己配
这样Ajax请求就会附带Cookie字段,简单请求,预检都会带
服务器端响应时,得在响应头添加,Access-Control-Allow-Credentials:true
如果没有,浏览器即视为跨域拒接
上面说过服务器响应Access-Control-Allow-Origin
不能设为“*”,就是因为当需要附带身份认证时,必须有具体的Origin
源,
服务器端
另外在跨域访问时,js只能读取到以下七种
Cache-Control
Content-Language
Content-Length
Content-Type
Expires
Last-Modified
Pragma
需要在服务器端配置Access-Control-Expose-Headers
例如,Access-Control-Expose-Headers:i,a,b'
结尾
其实CORS已经有大佬写好写好了,直接拿来用就行了…
写的不错哦