文章目录
一、什么是跨域
1. 同源策略
- 跨域是指一个源下的文档或脚本想要去请求另一个源下的资源。
- 非同源请求,也就是协议、端口、主机其中一项不相同的时候。
- 比如:
<link><script><img>
等Dom标签,还有在样式中嵌入的文件外链,以及JS发起的ajax请求等都存在跨域。
2. 目的
- 主要是用来防止 CSRF 攻击的。简单点说,CSRF 攻击是利用用户的登录态发起恶意请求。
3. 哪些不会被跨域限制?
- 页面中的链接,重定向以及表单提交是不会受到同源策略限制的。
- 我们还发现凡是拥有”src”这个属性的标签都拥有跨域的能力,比如<\script>、<\img>、<\iframe>
三、4 种跨域情况
当协议、子域名、主域名、端口号中任意一个不相同时,都算作不同域。
- 不同域名属于跨域,如:www.a.com 和www.b.com,另外www.a.com 和www.a.com.cn也属于不同域名。
- 主域名和子域名(二级域名、三级域名等)跨域,如:www.a.com 和 bbs.a.com 跨域。
- 不同协议属于跨域,如:http://www.a.com 和 https://www.a.com。
- Ip和域名属于跨域,如:123.23.23.12 和 www.a.com。
四、访问限制
除了跨域请求之外,还有以下限制
储存
- 当前域下的 js 脚本不能够访问其他域下的 cookie、localStorage 和 indexDB。浏览器中存储用户数据时,会标记数据的来源(origin),当JS脚本访问本地数据时,会检查它的orign是否一致,不一致就拒绝访问。
操作DOM
- 当前域下的 js 脚本不能够操作访问操作其他域下的 DOM。
五、跨域解决方案
1. JSONP
(1)简介
JSONP全称叫
JSON with Padding
,将数据填充进回调函数,是一个非官方的协议。
(2)原理
简单来说,就是**利用
<script>
标签没有跨域限制**。
客户端
(请求地址、参数(包含回调函数名)、全局函数(接收数据))
- 全局定义一个函数A,用于处理响应数据。(script)
- script标签的src属性中,设置请求地址和参数,将函数A的函数名作为参数callback的值进行传递。
服务端
- 收到请求后,返回一个函数A的调用,将响应数据传入函数A中。
结果
客户端接收到响应就相当于:
- 页面中定义函数A,对传入的参数进行处理。
- 用来发送请求是script相当于:调用函数A,将响应数据作为函数的参数。
注意:请求中callback这个键名需要和后端约定好。
**(3)代码演示 **
客户端
<script>var script = document.createElement('script');
script.type ='text/javascript';// 传参一个回调函数名给后端,方便后端返回时执行这个在前端定义的回调函数
script.src ='http://www.domain2.com:8080/login?user=admin&callback=handleCallback';
document.head.appendChild(script);// 回调执行函数functionhandleCallback(res){alert(JSON.stringify(res));}</script>
服务端返回内容
handleCallback({"success":true,"user":"admin"})
(4)特点
- 具有局限性, 仅支持get方法
- 不安全,可能会遭受XSS攻击
2. CORS
(1)简介
- CORS是一个W3C标准,全称是跨域资源共享,它允许浏览器发出跨域请求。
- 它需要浏览器和后端同时支持。目前所有浏览器都支持CORS 通信(IE浏览器不低于IE10),实现 CORS 通信的关键是后端。
- 简单来说,如果遇到跨域请求,浏览器能够发出请求,但是需要服务端支持,如果服务端不支持,返回的内容就会被浏览器拦截。
(2)实现
- 服务端设置 Access-Control-Allow-Origin 就可以开启 CORS。 该属性表示哪些域名可以访问资源,如果设置通配符则表示所有网站都可以访问资源。
(3)简单请求
请求过程
- 对于简单请求,浏览器会直接发出CORS请求,它会在请求的头信息中增加一个Origin字段,该字段用来说明本次请求来自哪个源(协议+端口+域名).
- 服务器会根据这个值来决定是否同意这次请求。如果在允许范围内,响应信息中额外携带 Access-Control-Allow-Origin字段,如果不再允许范围内,就没有这个头部信息。浏览器根据这个头部来判断是否要展示数据。如果没有这个响应头部信息,数据就会被拦截。
使用以下请求方法属于简单请求
GET
HEAD
POST
满足简单请求条件的还有很多情况,这里不一一列举。
(4)复杂请求
- 非简单请求是对服务器有特殊要求的请求,比如请求方法为DELETE或者PUT等。
- 非简单请求的CORS请求会在正式通信之前进行一次HTTP查询请求,称为预检请求。
- 在这个预检请求,是浏览器自动发出,询问服务器当前所在的网页是否在服务器允许访问的范围内,以及可以使用哪些HTTP请求方式和头信息字段,只有得到肯定的回复,才会进行正式的HTTP请求。(否则就会报错)
- 预检请求使用的请求方法是OPTIONS,表示这个请求是来询问的。他的头信息中的关键字段是Origin,表示请求来自哪个源。除此之外,头信息中还包括请求方法和会额外发送的头信息。(两个字段:- Access-Control-Request-Method:该字段是必须的,用来列出浏览器的CORS请求会用到哪些HTTP方法。- Access-Control-Request-Headers: 该字段是一个逗号分隔的字符串,指定浏览器CORS请求会额外发送的头信息字段。)
- 服务器在收到浏览器的预检请求之后,会根据请求头部(源、请求方式等)进行判断,只有服务端返回允许请求的响应,浏览器才会发送正式的请求,否则报错。
(5)减少OPTIONS请求
- OPTIONS请求次数过多就会损耗页面加载的性能,降低用户体验度。可以缓存预检结果。
- 通过响应头来设置预检请求结果的缓存时间。
- 要减少OPTIONS请求次数,可以后端在请求的返回头部添加:Access-Control-Max-Age:number。它表示预检请求的返回结果可以被缓存多久,单位是秒。
- 该字段只对完全一样的URL的缓存设置生效,所以设置了缓存时间,在这个时间范围内,再次发送请求就不需要进行预检请求了。
(6)Cookie
- 默认情况下在CORS请求是不带 cookie 的。
- 需要在服务端和客户端都进行设置同意。
- 在客户端,是在代码中设置 XMLHttpRequest 的 withCrendetails 属性
- 服务端进行相应的设置就可以发送cookie了。
(7)优缺点
优点
- 支持所有http请求
- 安全性比JSONP高
- 配置简单
缺点
- 不兼容老版本浏览器(IE浏览器不低于IE10)
- 需要服务端支持
(8)浏览器拦截
跨域请求发出:同CORS请求(有简单请求和复杂请求)
浏览器对跨域拦截:
- 如果响应头中没有允许跨域访问的配置,则不加载,并报出响应异常
- 如果响应头中有允许跨域访问的设置,则正常加载数据。
3. Proxy
- 代理(Proxy)也称网络代理,是一种特殊的网络服务,允许一个(一般为客户端)通过这个服务与另一个网络终端(一般为服务器)进行非直接的连接。
- 同源策略是浏览器的限制,服务端没有这个限制,也就是说从一个服务端向另一个服务端发起请求并无跨域。
- 一般认为代理服务有利于保障网络终端的隐私或安全,防止攻击。
方案一
- 如果是通过
vue-cli
脚手架工具搭建项目,我们可以通过webpack
为我们起一个本地服务器作为请求的代理对象 - 通过该服务器转发请求至目标服务器,得到结果再转发给前端,但是最终发布上线时如果web应用和接口服务器不在一起仍会跨域在
vue.config.js
文件,新增以下代码amodule.exports ={devServer:{host:'127.0.0.1',port:8084,open:true,// vue项目启动时自动打开浏览器proxy:{'/api':{// '/api'是代理标识,用于告诉node,url前面是/api的就是使用代理的target:"http://xxx.xxx.xx.xx:8080",//目标地址,一般是指后台服务器地址changeOrigin:true,//是否跨域pathRewrite:{// pathRewrite 的作用是把实际Request Url中的'/api'用""代替'^/api':""}}}}}
通过axios
发送请求中,配置请求的根路径axios.defaults.baseURL = '/api'
方案二
- 通过node服务端实现代理请求转发,以
express
框架为例var express = require('express');const proxy = require('http-proxy-middleware')const app = express()app.use(express.static(__dirname + '/'))app.use('/api', proxy({ target: 'http://localhost:4000', changeOrigin: false }));module.exports = app
方案三
- 通过配置
nginx
实现代理server { listen 80; # server_name www.josephxia.com; location / { root /var/www/html; index index.html index.htm; try_files $uri $uri/ /index.html; } location /api { proxy_pass http://127.0.0.1:3000; proxy_redirect off; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; }}
4. 总结
- CORS支持所有类型的HTTP请求,是跨域HTTP请求的根本解决方案JSONP只支持GET请求
- JSONP的优势在于支持老式浏览器,以及可以向不支持CORS的网站请求数据。
- 不管是Node中间件代理还是nginx反向代理,主要是通过同源策略对服务器不加限制。
- 日常工作中,用得比较多的跨域方案是
CORS
和Nginx
反向代理
版权归原作者 Palate 所有, 如有侵权,请联系我们删除。