谈谈跨域、同源策略、以及常见跨域解决方案

689 阅读5分钟

什么是跨域?

跨域:是指浏览器不能执行其他网站的脚本。它是由浏览器的同源策略造成的,是浏览器对js实施的安全限制。

同源策略

同源策略是一个安全策略。所谓的同源,指的是协议,域名、端口号相同。  

同源策略:

浏览器处于安全方面的考虑,只允许本域名下的接口交互,不同源的客户端脚本,在没有明确授权的情况下,不能读写对方的资源。

限制了一下行为:

  • Cookie、LocalStorage 和 IndexDB 无法读取
  • DOM 和 JS 对象无法获取 
  • Ajax请求发送不出去 

 解决方案

当然了,我梳理了几个我觉得工作中常用的,其他的自行去了解。

 jsonp跨域

利用script标签没有跨域限制的漏洞,网页可以拿到从其他来源产生动态JSON数据,当然了JSONP请求一定要对方的服务器做支持才可以。

「与AJAX对比」

「JSONP优点」 

兼容性比较好,可用于解决主流浏览器的跨域数据访问的问题。缺点就是仅支持get请求,具有局限性,不安全,可能会受到XSS攻击。

「思路」

  1. 创建script标签 

  2. 设置script标签的src属性,以问号传递参数,设置好回调函数callback名称 

  3. 插入html文本中

  4. 调用回调函数,res参数就是获取的数据

    function jsonp({ url, params, cb }) { return new Promise((resolve, reject) => { window[cb] = function (data) { resolve(data); document.body.remove(script); } params = {...params,cb}; let arr = []; for(let key in params){ arr.push(${key}=${params[key]}); } let script = document.createElement('script'); script.src = ${url}?${arr.join('&')}; document.body.appendChild(script); }) }

当然,jquery也支持jsonp的实现方式

 $.ajax({ url: 'http://www.baidu.cn/login', type: 'GET', dataType: 'jsonp', //请求方式为jsonp jsonpCallback: 'callback', data: { "username": "Nealyang" } }) 

「JSONP优点」

  • 它不像XMLHttpRequest对象实现的Ajax请求那样受到同源策略的限制
  • 它的兼容性更好,在更加古老的浏览器中都可以运行,不需要XMLHttpRequest或ActiveX的支持
  • 并且在请求完毕后可以通过调用callback的方式回传结果。

「JSONP缺点」

  • 它只支持GET请求而不支持POST等其它类型的HTTP请求
  • 它只支持跨域HTTP请求这种情况,不能解决不同域的两个页面之间如何进行JavaScript调用的问题

 跨域资源共享 CORS

 CORS(Cross-Origin Resource Sharing)跨域资源共享,定义了必须在访问跨域资源时,浏览器与服务器应该如何沟通。CORS背后的基本思想就是使用自定义的HTTP头部让浏览器与服务器进行沟通,从而决定请求或响应是应该成功还是失败。目前,所有浏览器都支持该功能,IE浏览器不能低于IE10。整个CORS通信过程,都是浏览器自动完成,不需要用户参与。对于开发者来说,CORS通信与同源的AJAX通信没有差别,代码完全一样。浏览器一旦发现AJAX请求跨源,就会自动添加一些附加的头信息,有时还会多出一次附加的请求,但用户不会有感觉。 上面是引用,你要记住的关键点

  • 浏览器会自动进行 CORS 通信,实现 CORS 通信的关键是后端。只要后端实现了 CORS,就实现了跨域。
  • 服务端设置 Access-Control-Allow-Origin 就可以开启 CORS。该属性表示哪些域名可以访问资源,如果设置通配符则表示所有网站都可以访问资源。

 请求分为**「简单请求」「非简单请求」**,所以我们的了解这两种情况。

「简单请求」

 满足下面两个条件,就属于简单请求

 条件1:使用下列方法之一:  

  • GET 
  • HEAD
  • POST 

条件2:Content-Type 的值仅限于下列三者之一

  • text/plain 
  • multipart/form-data 
  • application/x-www-form-urlencoded

 请求中的任意 XMLHttpRequestUpload 对象均没有注册任何事件监听器; XMLHttpRequestUpload 对象可以使用 

XMLHttpRequest.upload 属性访问。

** 「复杂请求」**

 不符合以上条件的请求就肯定是复杂请求了。复杂请求的CORS请求,会在正式通信之前,增加一次HTTP查询请求,称为"预检"请求,该请求是 option 方法的,通过该请求来知道服务端是否允许跨域请求。

 直接上一个例子吧 看看一个完整的复杂请求吧,并且介绍一下CORS请求的字段。 

//server2.js
let express = require('express') 
let app = express()
let whitList = ['http://localhost:3000'] //设置白名单 
app.use(function(req, res, next) {
 let origin = req.headers.origin
 if (whitList.includes(origin)) { 
res.setHeader('Access-Control-Allow-Origin', origin) // 设置哪个源可以访问我res.setHeader('Access-Control-Allow-Headers', 'name')// 允许携带哪个头访问我 
 res.setHeader('Access-Control-Allow-Methods', 'PUT')  // 允许哪个方法访问我
res.setHeader('Access-Control-Allow-Credentials', true) // 允许携带cookie 
 res.setHeader('Access-Control-Max-Age', 6) // 预检的存活时间
 res.setHeader('Access-Control-Expose-Headers', 'name') // 允许返回的头
if (req.method === 'OPTIONS') { res.end() // OPTIONS请求不做任何处理 }
 }
 next() }) 
app.put('/getData', function(req, res) {
 console.log(req.headers) 
res.setHeader('name', 'jw') //返回一个响应头,后台需设置 res.end('我不爱你') 
}) app.get('/getData', function(req, res) {
 console.log(req.headers) res.end('我不爱你') 
})
 app.use(express.static(__dirname))
app.listen(4000) 

上述代码由_http://localhost:3000/index.html向http://localhost:4000_/跨域请求,正如我们上面所说的,后端是实现 CORS 通信的关键。

 上述的例子,一定对你会有所帮助的,这块代码,是跟着浪里行舟代码来的,参考处注明了出处。

_ 「与JSONP对比」_

  • JSONP只能实现GET请求,而CORS支持所有类型的HTTP请求。 
  • 使用CORS,开发者可以使用普通的XMLHttpRequest发起请求和获得数据,比起JSONP有更好的错误处理。 
  • JSONP主要被老的浏览器支持,它们往往不支持CORS,而绝大多数现代浏览器都已经支持了CORS)