同源策略,它是由Netscape提出的一个著名的安全策略。如今全部支持JavaScript 的浏览器都会使用这个策略来对脚本和请求进行校验,若不一样源,则禁止使用。html
那若是判断是否同源?主要根据三个维度,域名,协议,端口三个都相同才算同源。
举个🌰:前端
网站A | 网站B | 结果 |
---|---|---|
http://www.zhenai.com | http://i.zhenai.com | 不一样源,域名不一样 |
http://www.zhenai.com | http://www.zhenai.cn | 不一样源,域名不一样 |
http://www.zhenai.com | https://www.zhenai.com | 不一样源,协议不一样 |
http://www.zhenai.com | http://www.zhenai.com:3000 | 不一样源,端口不一样(默认端口80) |
这个主要是为了防止恶意网站经过js获取用户其余网站的cookie等用户信息。vue
防止恶意网站经过iframe获取页面dom,从而窃取页面的信息。node
防止恶意的请求攻击服务器窃取数据信息。nginx
那是否是说非同源的请求就没法实现呢?也不是,这就引出了咱们本文主要阐述的解决跨域请求问题的方法。ajax
jsonp能实现跨域是利用了img、script和link标签自身的跨域能力。
咱们知道当img或者script中的src是一个连接的时候,浏览器会请求这个连接获取资源,那么这个连接若是是跨域的,浏览器也会请求,从而达到了跨域请求的一个功能。npm
var script = document.createElement('script'); script.src = 'http://localhost:3000/api/test.do?a=1&b=2&callback=cb'; $('body').append(script); function cb(res){ // do something console.log(res) }
能够看到,咱们建立一个script标签,将src改为咱们要请求的接口,并将script添加在body中,那么当浏览器解析到这个script时,会想src对应的服务器发送一个get请求,并将参数带过去。
而后当浏览器接收到服务端返回的数据,就会触发参数中callbak对应的回调函数cb,从而完成整个get请求。json
简单粗暴segmentfault
①只支持get请求
②须要后台配合,将返回结果包装成callback(res)的形式后端
那若是黑客植入script脚本经过jsonp的方式对服务器进行攻击,怎么办?
能够经过页面设置的内容安全协议csp
进行防范。
cors 是一个W3C标准,全称是"跨域资源共享"(Cross-origin resource sharing),它容许浏览器向跨源服务器发送XMLHttpRequest请求,从而克服了 AJAX 只能同源使用的限制
cors 须要浏览器和服务器同时支持,整个 CORS通讯过程,都是浏览器自动完成不须要用户参与,对于开发者来讲,cors的代码和正常的 ajax 没有什么差异,浏览器一旦发现跨域请求,就会添加一些附加的头信息
可是,cors不支持ie10及如下版本。
浏览器将cors请求分为简单请求和复杂请求。
简单请求则直接发送请求到服务器,只请求一次。
而复杂请求在正式请求前都会有预检请求,在浏览器中都能看到有OPTIONS请求,用于向服务器请求权限信息的,须要请求两次。
那如何区分是简单请求仍是复杂请求呢?
简单请求必需要同时知足下面三个条件:
类型 | 描述 |
---|---|
application/json | 消息主体是序列化后的 JSON 字符串 |
application/x-www-form-urlencoded | 数据被编码为键值对。这是标准的编码格式 |
multipart/form-data | 须要在表单中进行文件上传时,就须要使用该格式。常见的媒体格式是上传文件之时使用的 |
text/plain | 数据以纯文本形式(text/json/xml/html)进行编码,其中不含任何控件或格式字符 |
application/json:
application/x-www-form-urlencoded:是Jquery的Ajax请求默认方式
不知足简单请求的条件,那么就是复杂请求。
复杂请求会在正式请求发送以前,先发一个预检请求进行校验,校验经过后才能进行正式请求。
举个🌰
浏览器如今要发送一个put的复杂请求,那么在put请求发送以前,浏览器先发送一个options请求。
options请求头信息:
OPTIONS /cors HTTP/1.1 Origin: localhost:3000 Access-Control-Request-Method: PUT // 表示使用的什么HTTP请求方法 Access-Control-Request-Headers: X-Custom-Header // 表示浏览器发送的自定义字段 Host: localhost:3000 Accept-Language: zh-CN,zh;q=0.9 Connection: keep-alive User-Agent: Mozilla/5.0...
服务器收到options请求之后,检查了Origin、Access-Control-Request-Method和Access-Control-Request-Headers字段之后,确认容许跨源请求,就能够作出回应
options响应头信息
HTTP/1.1 200 OK Date: Mon, 01 Dec 2008 01:15:39 GMT Server: Apache/2.0.61 (Unix) Access-Control-Allow-Origin: http://localhost:3000 // 表示http://localhost:3000能够访问数据 Access-Control-Allow-Methods: GET, POST, PUT Access-Control-Allow-Headers: X-Custom-Header Content-Type: text/html; charset=utf-8 Content-Encoding: gzip Content-Length: 0 Keep-Alive: timeout=2, max=100 Connection: Keep-Alive Content-Type: text/plain
当options请求经过以后发出正式的HTTP请求,假若options请求不经过,则服务器不容许这次访问,从而抛出错误
options请求经过以后的,浏览器发出发请求
PUT /cors HTTP/1.1 Origin: http://api.zhenai.com Host: api.alice.com X-Custom-Header: value Accept-Language: en-US Connection: keep-alive User-Agent: Mozilla/5.0...
那这样的话,若是页面存在大量的复杂请求,岂不是每一个请求前面都要进行一次options的请求,那不会形成大量资源的浪费么?
若是基于cors请求的方法来解决跨域问题,那么复杂请求以前是须要进行一个options的请求的,但咱们能够经过对options请求进行缓存来减轻请求的压力。
在options请求中,咱们能够经过设置响应头的参数Access-Control-Max-Age
来对结果进行缓存
好比: Access-Control-Max-Age: 600
表示对options检验结果进行十分钟的缓存
该字段的兼容性以下:
nginx解决跨域的问题跟以前的方法有所不一样,它是经过服务器的方向代理,将前端访问域名跟后端服务域名映射到同源的地址下,从而实现前端服务和后端服务的同源,那天然不存在跨域的问题了。
举个🌰:
前端服务:http://localhost:3000
,
前端页面路由:http://localhost:3000/page.html
,
后端服务:http://localhost:3001
,
后端接口路由:http://localhost:3001/api/test.do
能够看出,两个服务处于跨域的状态
经过nginx的配置进行反向代理,便可实现先后端服务同源,以下:
server { listen 80; server_name localhost; location = / { proxy_pass http://localhost:3000; } location /api { proxy_pass http://localhost:3001; #指定容许跨域的方法,*表明全部 add_header Access-Control-Allow-Methods *; #预检命令的缓存,若是不缓存每次会发送两次请求 add_header Access-Control-Max-Age 3600; #带cookie请求须要加上这个字段,并设置为true add_header Access-Control-Allow-Credentials true; #表示容许这个域跨域调用(客户端发送请求的域名和端口) #$http_origin动态获取请求客户端请求的域 不用*的缘由是带cookie的请求不支持*号 add_header Access-Control-Allow-Origin $http_origin; #表示请求头的字段 动态获取 add_header Access-Control-Allow-Headers $http_access_control_request_headers; #OPTIONS预检命令,预检命令经过时才发送请求 #检查请求的类型是否是预检命令 if ($request_method = OPTIONS){ return 200; } } }
其实nginx不只仅只是用于解决跨域问题,而是涉及到不少服务器资源分配的处理,在此就不详细探讨了。
其实,在咱们主流使用的MVVM框架中,配置项里面也提供解决跨域问题的能力,继续举个🌰,以vue2.x为例,咱们能够经过在config/index.js
中添加配置项实现跨域请求:
proxyTable: { '/apis': { // 测试环境 target: 'http://www.zhenai.com/', // 接口域名 changeOrigin: true, //是否跨域 pathRewrite: { '^/apis': '' //须要rewrite重写的, } } }
其实原理很简单,就是在咱们使用npm run dev
命中,启动了一个node服务,而后将前端发出的请求发送到node服务,再将该服务转发到本来的后台服务,在这过程当中实现了一层代理,由一个node服务发送一个请求到另一个后台服务,天然也没有了浏览器所限制的跨域问题。
https://blog.csdn.net/yingwang9/article/details/90716623
https://www.jianshu.com/p/d89c62572acd
https://segmentfault.com/a/1190000019227927?utm_source=tag-newest