谈谈跨域、同源策略、以及常见跨域解决方案
跨域:是指浏览器不能执行其他网站的脚本。它是由浏览器的同源策略造成的,是浏览器对js实施的安全限制。
同源策略
同源策略是一个安全策略。所谓的同源,指的是协议,域名、端口号相同。
限制了一下行为:
- Cookie、LocalStorage 和 IndexDB 无法读取
- DOM 和 JS 对象无法获取
- Ajax请求发送不出去
解决方案 :
当然了,我梳理了几个我觉得工作中常用的,其他的自行去了解。
jsonp跨域
利用script标签没有跨域限制的漏洞,网页可以拿到从其他来源产生动态JSON数据,当然了JSONP请求一定要对方的服务器做支持才可以。
「与AJAX对比」
「JSONP优点」
兼容性比较好,可用于解决主流浏览器的跨域数据访问的问题。缺点就是仅支持get请求,具有局限性,不安全,可能会受到XSS攻击。
-
创建script标签
-
设置script标签的src属性,以问号传递参数,设置好回调函数callback名称
-
插入html文本中
-
调用回调函数,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( k e y = {key}= key={params[key]}); } let script = document.createElement(‘script’); script.src = u r l ? {url}? 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)
「与JSONP对比」
- JSONP只能实现GET请求,而CORS支持所有类型的HTTP请求。
- 使用CORS,开发者可以使用普通的XMLHttpRequest发起请求和获得数据,比起JSONP有更好的错误处理。
- JSONP主要被老的浏览器支持,它们往往不支持CORS,而绝大多数现代浏览器都已经支持了CORS)