什么是同源策略,前端解决跨域问题的方法

同源策略

同源策略/SOP(Same origin policy)是一种约定,是浏览器最核心也最基本的安全功能,现在所有支持 JavaScript 的浏览器都会使用这个策略。如果缺少了同源策略,浏览器很容易受到 XSS、 CSFR 等攻击。

同源是指"协议+域名+端口"三者相同,即便两个不同的域名指向同一个 ip 地址,也非同源。源就是协议、域名和端口号。

1.非同源的限制

当一个页面中使用XMLHTTPRequest(XHR请求)对象发送HTTP请求时,必须保证当前页面和请求的对象是同源的,即协议、域名和端口号要完全一致,否则浏览器就会阻止此跨域请求返回的数据。

2.什么是跨域?

由于浏览器为了防止CSRF攻击(Cross-site request forgery跨站请求伪造),避免恶意攻击而带来的风险而采取的同源策略限制。

当一个请求url的协议、域名、端口三者之间任意一个与当前页面url不同即为跨域

如何解决跨域

1.后端解决

CORS (Cross-Origin Resource Sharing),跨域资源共享。CORS是官方的跨域解决方案,它的特点是不需要在客户端做任何特殊的操作,完全在服务器中进行处理,支持 get 和 post 请求。跨域资源共享标准新增了一组HTTP首部字段,允许服务器声明哪些源站通过浏览器有权限访问哪些资源|
CORS是通过设置一个响应头来告诉浏览器,该请求允许跨域,浏览器收到该响应以后就会对响应放行。

response.setHeade('Access-Control-Allow-Origin','*')  //设置所有的请求地址都允许跨域
response.setHeade('Access-Control-Allow-Origin','http//127.0.0.1:5500')  //只有127.0.0.1:5500允许跨域
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>
    function fn(res){
      console.log(res);
    }
</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>

 

posted @ 2022-08-24 20:04  Lamb~  阅读(973)  评论(0编辑  收藏  举报