同源策略
同源策略/SOP(Same origin policy)是一种约定,是浏览器最核心也最基本的安全功能,现在所有支持 JavaScript 的浏览器都会使用这个策略。如果缺少了同源策略,浏览器很容易受到 XSS、 CSFR 等攻击。
同源是指"协议+域名+端口"三者相同,即便两个不同的域名指向同一个 ip 地址,也非同源。源就是协议、域名和端口号。1.非同源的限制
当一个页面中使用XMLHTTPRequest(XHR请求)对象发送HTTP请求时,必须保证当前页面和请求的对象是同源的,即协议、域名和端口号要完全一致,否则浏览器就会阻止此跨域请求返回的数据。
2.什么是跨域?
由于浏览器为了防止CSRF攻击(Cross-site request forgery跨站请求伪造),避免恶意攻击而带来的风险而采取的同源策略限制。
当一个请求url的协议、域名、端口三者之间任意一个与当前页面url不同即为跨域如何解决跨域
1.后端解决
response.setHeade('Access-Control-Allow-Origin','*') //设置所有的请求地址都允许跨域
response.setHeade('Access-Control-Allow-Origin-Method','*') //设置所有的请求方法都允许跨域
2.前端解决
- jsonp(常用)
- 代理服务器(常用)
- websocket(套接字,走的是tcp-ip协议)
- 通过 iframe script link 标签请求资源(src、href)
jsonp 实现跨域请求
浏览器对script标签src属性、link标签ref属性和img标签src属性等没有这这种限制,利用这个“漏洞”就可以很好的解决跨域请求。JSONP就是利用了script标签无同源限制的特点来实现的,当向第三方站点请求时,我们可以将此请求放在<script>标签的src属性里,这就如同我们请求一个普通的JS脚本,可以自由的向不同的站点请求
通过在请求的 url 后指定一个回调函数,然后服务器在返回数据的时候,会构建一个 json 数据的包装,这个包装就是回调函数,然后返回给前端,前端接收到数据后,因为请求的是脚本文件,所以会直接执行,这样我们先前定义好的回调函数就可以被调用,从而实现了跨域请求的处理。这种方式只能用于 get 请求。
JSONP的优点:
- 它不像XMLHttpRequest对象实现的Ajax请求那样受到同源策略的限制;
- 它的兼容性更好,在很多老版本的浏览器中都可以运行,不需要XMLHttpRequest或ActiveX的支持;
- 并且在请求完毕后可以通过回调函数回传结果;
- 由于只支持get请求,所以它的速度较快。
JSONP的缺点:
它只支持GET请求而不支持POST等其它类型的HTTP请求; 所以jsonp使用在查询场景居多
实现示例
百度搜索接口:https://sp0.baidu.com/5a1Fazu8AA54nxGko9WTAnF6hhy/su?wd=miqi&cb=fn
wd表示关键词,cb表示回调函数。通过回调函数可以将响应的结果拿到
<script> // var wd = '奥特曼' function fn(result){ console.log(result); } </script> <script src="https://sp0.baidu.com/5a1Fazu8AA54nxGko9WTAnF6hhy/su?wd=奥特曼&cb=fn"></script>
JSONP的封装
普通封装
function jsonp(url,param={},paramName,callback){ if(typeof url != 'string'){ throw new Error('url必须为字符串') } url += '?' // 将param转为&拼接 for(let key in param){ url += `&${key}=${param[key]}` } //函数名需要加工(保持的函数名的唯一) let callbackName = 'fn' + Date.now() + Math.ceil(Math.random()*10) //加给对应的window window[callbackName] = callback //将参数名和回调函数名传入 url += `&${paramName}=${callbackName}` //创建一个script标签 let script = document.createElement('script') //指定对应的src地址 script.src = url //加给body document.body.appendChild(script) //script标签加载完毕 script.onload = function(){ this.remove() //将script标签删除 delete window[callbackName] //将对应的属性删除 } }
测试代码:
<body> <input type="text" class="input" /> <ul class="showBox"></ul> <script src="./jsonp.js"></script> <script> let inp = document.querySelector('.input'); let showBox = document.querySelector('.showBox'); inp.oninput = function () { let wd = this.value; if (wd) { jsonp( 'https://sp0.baidu.com/5a1Fazu8AA54nxGko9WTAnF6hhy/su', { wd, }, 'cb', function (res) { showBox.innerHTML = ''; res.s.forEach((v) => { showBox.innerHTML += `<li>${v}</li>`; }); } ); } else { showBox.innerHTML = ''; } }; </script> </body>
使用promise封装
function jsonp(url,param={},paramName){ return new Promise((resolve,reject)=>{ if(typeof url != 'string'){ throw new Error('url必须为字符串') } url += '?' // 将param转为&拼接 for(let key in param){ url += `&${key}=${param[key]}` } //函数名需要加工(保持的函数名的唯一) let callbackName = 'fn' + Date.now() + Math.ceil(Math.random()*10) //加给对应的window window[callbackName] = resolve //resolve里面的参数会被then接收 这个resolve会被服务器自动调用并传入参数 //将参数名和回调函数名传入 url += `&${paramName}=${callbackName}` //创建一个script标签 let script = document.createElement('script') //指定对应的src地址 script.src = url //加给body document.body.appendChild(script) //script标签加载完毕 script.onload = function(){ this.remove() //将script标签删除 delete window[callbackName] //将对应的属性删除 } //script报错的时候 script.onerror = function(err){ reject('错误'+err) } }) }
测试代码:
<body> <input type="text" class="input" /> <ul class="showBox"></ul> <script src="./jsonpPromise.js"></script> <script> let inp = document.querySelector('.input'); let showBox = document.querySelector('.showBox'); inp.oninput = function () { let wd = this.value; if (wd) { jsonp( 'https://sp0.baidu.com/5a1Fazu8AA54nxGko9WTAnF6hhy/su', { wd, }, 'cb' ).then((res) => { showBox.innerHTML = ''; res.s.forEach((v) => { showBox.innerHTML += `<li>${v}</li>`; }); }); } else { showBox.innerHTML = ''; } }; </script> </body>
标签:请求,script,url,前端,let,showBox,jsonp,跨域 From: https://www.cnblogs.com/qianduanLamp/p/16616977.html