那些年的跨域问题

同源策略

  • 同源策略是由浏览器给的
  • 浏览器不允许我们向别人发送请求,只能向自己的服务器发送请求
  • 当我们想向别人的服务器发送请求的时候,就会被浏览器阻止了
  • 什么是 “别人的服务器” 呢?
    • 当 请求协议/域名/端口号 有任意一个不同的时候,那么就算是别人的服务器
    • 这个时候就会触发同源策略
  • 我们管触发了 同源策略 的请求叫做 跨域请求

跨域

跨域(非同源策略请求)

  • 同源策略请求 ajax/fetch
  • 跨域传输

早期前部署到web服务器上:同源策略

补充:js是一门客户端语言 :在客户端执行的语言

1、jsonp解决跨域 :前提是服务端的支持

2、cors 解决跨域 :前提是服务端的支持

3、服务端代理解决跨域 :目标服务器有接口,并且没有权限

为什么解决跨域

因为有时候不可能仅仅使用自己的数据,当我们想要使用别人的数据接口时,就比如后端给我们的数据接口,说不定使用过程中就跨域了,所以必须解决跨域问题。

xampp
假如本地服务地址是 http://127.0.0.1:8080/index.html
服务端请求数据的地址 http://api.xxxx.com/getData
协议是一样的,但是域名不一样,端口也不一样,这样就导致访问不到数据了
之前的解决方法是:
修改本地的host文件,如127.0.0.1:8080然后定位到http://api.xxxx.com/

服务器拆分
web服务器:静态资源 xx.xx.xx.com
data服务器:业务逻辑和数据分析 api.xx.xx.xx.com
图片服务器

三者都一样就是同源,只要有一个不同就是跨域

  • 协议
  • 域名
  • 端口号

web服务器地址:http:127.0.0.1:8080/index.html(想在这个地址里面有数据请求)
数据接口地址:http://127.0.0.1:8000/list
协议一样,域名一样,端口号不一样 —— 跨域

1、JSONP跨域解决

先说下简单理解jsonp的跨域问题

<!DOCTYPE html>
<html lang="en">

<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Document</title>
</head>

<body>
  <script>

    // jsonp  json 
    // jsonp 是被包裹的 json 数据;
    // json :{"a" : 10}
    // jsonp :callback({"a" : 10})

    // 跨域 :  xhr被限制了;
    // 除了xhr,还有没有其他的可以发送http请求的方式那 ?
    // 发现 带有 src 的标签可以发起http请求,同时不受同源策略的限制。

    // img     ×  
    // script  √ 

    // 这里为什么不能 用img 
    // 因为浏览器之中带标签 加载数据之后都会按照标签所表示的信息进行解析;
    // 因为我们目标 : 加载数据在javascript之中使用;

    // 我们需要使用 script 发请求,让代码当成script标签进行解析。

    //当前的标签要用数据, 外部的标签进行数据的传入;
    // 1. 全局变量可以访问;
    // 2. 回调函数; 
    // 3. 函数存在参数传递;

    // jsonp的跨域方案 : 
    // 1. 前端定义一个全局函数;
    // 2. script标签使用src发起一个跨域请求;
    // 3. 请求的内容调用前端函数。
    // 4. 调用时传入实参,在前端全局函数使用形参接受;


    // 定义一个函数 
    function callback(data) {
      console.log(data)
    }

  </script>

  <!-- 访问 -->
  <!-- 拆分出去; 拆到任意服务器之中进行请求处理; -->
  <script src="http://127.0.0.1:80/0713/jsonp.js"></script>

  <script src="http://127.0.0.1:80/0713/jsonp.php"></script>
</body>

</html>
//jsonp.js文件
callback("这是jsonp.js")
//jsonp.php文件
<?php
  echo "callback('这是jsonp.php')";
?>

在这里插入图片描述
在这里插入图片描述
上面所说的标签中带有src的有这些:

  • script
  • img
  • link
  • iframe

  • =>不存在跨域请求的限制

在这里插入图片描述
再说明一下上述情况的其他变换

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Document</title>
</head>
<body>
  <script src="https://cdn.bootcdn.net/ajax/libs/jquery/3.5.0/jquery.min.js"></script>
  <script src="jsonp.js"></script>
</body>
</html>
// jsonp.js文件
$.ajax({
  url: 'http:127.0.0.1:8080/list',
  method: 'get',
  // 如果走jsonp需要加下面的代码,执行的是jsonp的请求
  dataType: 'jsonp',
  success: res=> {
    console.log(res);
    
  }
});
// serverJSONP.js文件
let express = require('express'),
  app = express();
app.listen(8080,_=> {
  console.log('ok!');
  
});
app.get('/list',(req,res) => {
  let {
    callback=Function.prototype
    // Function.prototype是匿名空函数
  } = req.query;
  let data = {
    code: 0,
    message:'你好世界'
  };
  // 1、这里需要把data数据转换为字符串,用到JSON.stringify() 先在后端执行了,而在前端拿到的是字符串,
  // 不是对象数据 , 注意这段代码应该先在后端处理好,然后发到前端去执行。
  // 2、注意全局函数的名字,因为这个名字虽然是后端发给我的,但是我想要改这个名字,我不知道后端的名字,
  // 所以这时,当我script 标签中src发请求时,把这个我定义的名字 传给后端 告诉后端这是我定义的名字,
  // 注意,这只是一个函数名字,并不是一个函数。
  res.send(`${callback}(${JSON.stringify(data)})`);
});

npm serverJSONP
显示OK!
在这里插入图片描述
在这里插入图片描述

注意:jsonp只能处理GET请求

补充jsonp一些知识点:

利用script中的src发送请求,这里的src中可以是.js,也可以不是.js,不管是什么类型的文件,只要响应返回的数据就可以。script请求返回的一定是js代码,还要注意一个全局函数的传递,因为js是需要下载到本地 ,然后在本地执行,那个函数也必须是全局函数。。。

2、CORS跨域资源共享

解决:后端允许服务器访问

第一种:先说明一种简单的情况,为了方便说明,代码比较简单明了

<!DOCTYPE html>
<html lang="en">

<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Document</title>
</head>

<body>
  <script>
    // xhr请求
    var xhr = new XMLHttpRequest()
    xhr.open("GET", "http://127.0.0.1/0710/05_CORS.php")
    xhr.send()
    xhr.onreadystatechange = function () {
      if (xhr.readyState === 4 && /^2\d{2}/.test(xhr.status)) {
        console.log(xhr.responseText)
      }
    }
  </script>
</body>

</html>

PHP文件

<?php
  //简单的一句打印
  echo "hello";
?>

报错信息如下:

通过http://127.0.0.1/0710/05_CORS.php 访问 XMLHttpRequest, from origin 'http://localhost’被CORS策略阻止:被请求的资源上没有 ‘Access-Control-Allow-Origin’ 头文件。
在这里插入图片描述
这里只需要加一句即可解决:

<?php
  //被请求的资源上没有'Access-Control-Allow-Origin'头文件,那就加上这句,让它有头文件
  header("Access-Control-Allow-Orign:http://localhost");

  echo "hello";
?>

在这里插入图片描述

另一种如下:
使用npm 下载axios npm install --save axios

  • 客户端(发送ajax/fetch请求)
    axios.defaults.baseURL = 'http://127.0.0.1:8888';
    axios.defaults.withCredentials = true;
    axios.defaults.header['Content-Type'] = 'application/x-www-form-urlencoded';
    axios.defaults.transformRequest = function (data){
      if(!data) return data;
      let result = ``;
      for(let attr in data){
        if(!data.hasOwnProperty(attr)) break;
        result +=`&${attr}=${data[attr]}`;
      }
      return result.substing(1);
    };
    axios.interceptors.response.use(function onFulfilled(response){
      return response.data;
    },function onRejected(reason) {
      return Promise.reject(reason);
    });
    axios.defaluts.validateStatus = function (status) {
      return /^(2|3)\d{2}$/.test(status);
    }
  • 服务端设置相关的头信息(需要处理option试探性请求)
 app.use((req,res,next) => {
      res.header("Access-Control-Allow-Origin","http://localhost:8000");
      res.header("Access-Control-Allow-Credentials",true);
      res.header("Access-Control-Allow-Header",
      "Content-Type,Content-Length,Authorization,Accept,X-Require-With");
      res.header("Access-Control-Allow-Methods",
      "PUT,POST,GET,DELETE,HEAD,OPTIONS");
      if (req.method === 'OPTIONS'){
        res.send('ok');
        return;
      }
      next();
    });

3、ngnix 反向代理(服务端代理)

注意我这里服务器代理是nginx服务器代理

这里需要操作配置

  找到nginx配置文件 : nginx.conf
  phpstudy_pro\Extensions\nginx.1.15.11\conf\vhosts\0localhost_端口号.conf

在这里插入图片描述
修改里面的代码段即可
在这里插入图片描述

4、后端代理(比如PHP)

注意:这里以聚合数据为例 ,因为可以使用或者选择的余地比较大。
里面有教程讲解使用,看官网说明即可

<!DOCTYPE html>
<html lang="en">

<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Document</title>
</head>

<body>
  <!-- 
      1. 数据来源 : 聚合数据;
      2. 聚合数据接口需要跨域请求 : 后端代理。
      3. 前端用 xhr 对象请求就行;
  -->
  
  <!-- 
    用户交互设计 :  
    输入框 
    查询按钮

    当用户点击查询按钮的时候,获取输入框之中的数据,把数据用xhr发送到聚合数据接口之中,接受响应数据,根据数据渲染页面;
  -->
  <p><input type="text" placeholder="请输入需要查询天气的城市" id="cityname"></p>
  <p><button id="search">查询</button></p>
  <div id="container">
    查询结果
  </div>

  <script>
    // 字段名 : city

    // 前端流程 :  事件 => 获取用户数据并处理成GET请求发送的形式 =>发送xhr请求 => 接收到响应之后渲染页面。
    var cityname = document.querySelector("#cityname");
    document.querySelector("#search").onclick = function () {
      // 重复触发 ; 
      var cityname_value = cityname.value;
      // console.log(cityname_value); 已测试没有问题;
      // 字段名=字段值
      var get_msg = "?city=" + cityname_value;
      // console.log(get_msg); 已测试没有问题;

      var xhr = new XMLHttpRequest();
      var url = "./01_weather.php";
      xhr.open("GET", url + get_msg);
      xhr.send();
      xhr.onreadystatechange = function () {
        if (xhr.readyState === 4 && /^2\d{2}/.test(xhr.status)) {
          var data = JSON.parse(xhr.responseText);
          console.log(data)
        }
      }
    }
  </script>
</body>
</html>

注意里面的字段名 : city,这里字段名是后端写好的,前端使用的时候,必须保持一致,不然请求时,数据得不到

<?php

header('Content-type:text/html;charset=utf-8');
 
//配置您申请的appkey
$appkey = "7395d3dc2048cba91aee204208fb3ec8";

$city = $_GET["city"];
 
//************1.根据城市查询天气************
$url = "http://op.juhe.cn/onebox/weather/query";
$params = array(
      "cityname" => "$city",//要查询的城市,如:温州、上海、北京
      "key" => $appkey,//应用APPKEY(应用详细页查询)
      "type" => "top",//返回数据的格式,xml或json,默认json
);
$paramstring = http_build_query($params);
$content = juhecurl($url,$paramstring);
$result = json_decode($content,true);

if($result){
    if($result['error_code']=='0'){
        echo json_encode($result);
    }else{
        echo $result['error_code'].":".$result['reason'];
    }
}else{
    echo "请求失败";
}
//**************************************************

/**
 * 请求接口返回内容
 * @param  string $url [请求的URL地址]
 * @param  string $params [请求的参数]
 * @param  int $ipost [是否采用POST形式]
 * @return  string
 */
function juhecurl($url,$params=false,$ispost=0){
    $httpInfo = array();
    $ch = curl_init();
 
    curl_setopt( $ch, CURLOPT_HTTP_VERSION , CURL_HTTP_VERSION_1_1 );
    curl_setopt( $ch, CURLOPT_USERAGENT , 'JuheData' );
    curl_setopt( $ch, CURLOPT_CONNECTTIMEOUT , 60 );
    curl_setopt( $ch, CURLOPT_TIMEOUT , 60);
    curl_setopt( $ch, CURLOPT_RETURNTRANSFER , true );
    curl_setopt($ch, CURLOPT_FOLLOWLOCATION, true);
    if( $ispost )
    {
        curl_setopt( $ch , CURLOPT_POST , true );
        curl_setopt( $ch , CURLOPT_POSTFIELDS , $params );
        curl_setopt( $ch , CURLOPT_URL , $url );
    }
    else
    {
        if($params){
            curl_setopt( $ch , CURLOPT_URL , $url.'?'.$params );
        }else{
            curl_setopt( $ch , CURLOPT_URL , $url);
        }
    }
    $response = curl_exec( $ch );
    if ($response === FALSE) {
        //echo "cURL Error: " . curl_error($ch);
        return false;
    }
    $httpCode = curl_getinfo( $ch , CURLINFO_HTTP_CODE );
    $httpInfo = array_merge( $httpInfo , curl_getinfo( $ch ) );
    curl_close( $ch );
    return $response;
}

?>

5、http proxy

webpack 和 webpack-dev-server
在webpack.config.js中代理方式

devServer:{
  port:3000,
  progress: true,
  contentBase:'./buid',
  proxy:{
    './':{
      target:'http://127.0.0.1:3001',
      changeOrign:true
    }
  }
}

6、postMessage

window.postMessage() 方法可以安全地实现跨源通信。

https://developer.mozilla.org/zh-CN/docs/Web/API/Window/postMessage

7、websocket io 协议跨域

实时通信的协议等

8、基于iframe方案 —document.domain

只能实现同一个主域,不同的子域之间的操作等

总之以上问题有些还没有在项目中真正搞明白,后期再慢慢补充吧

补充:怎么获取外部接口数据(这里是4、服务端代理)

前提的必须解决 4、ngnix 反向代理中修改里面的代码段。。。

<!DOCTYPE html>
<html lang="en">

<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Document</title>
</head>

<body>
  <div id="container"></div>
  <script>
    var xhr = new XMLHttpRequest()
    var url ="/dt?include_fields=top_comments%2Cis_root%2Csource_link%2Citem%2Cbuyable%2Croot_id%2Cstatus%2Clike_count%2Csender%2Calbum%2Creply_count&filter_id=%E6%90%9E%E7%AC%91%E8%90%8C%E5%AE%A0&start=24&_=1594378037461"
    xhr.open("GET", url)
    xhr.send()
    xhr.onreadystatechange = function () {
      if (xhr.readyState === 4 && /^2\d{2}/.test(xhr.status)) {
        console.log(xhr.responseText)
      }
    }
  </script>
</body>

</html>

在这里插入图片描述
成功获取数据,就可以开心的渲染数据到页面
在这里插入图片描述
既然拿到数据了,那就随便渲染一下吧

<!DOCTYPE html>
<html lang="en">

<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <meta name="referrer" content="no-referrer" />
  <title>Document</title>
  <style>
    ul,
    li {
      list-style: none;
    }

    img {
      width: 300px;
      height: 300px;
      display: block;
    }

    .lis {
      width: 300px;
      height: 400px;
      float: left;
    }
  </style>
</head>

<body>
  <div id="container">
    <ul class="list-ul">
      <!-- <li class="lis">
        <h2></h2>
        <img src="" alt="">
      </li> -->
    </ul>
  </div>
  <script>
    var cont = document.querySelector("#container")
    var list_ul = document.querySelector(".list-ul")
    var xhr = new XMLHttpRequest()
    var url = "/dt?include_fields=top_comments%2Cis_root%2Csource_link%2Citem%2Cbuyable%2Croot_id%2Cstatus%2Clike_count%2Csender%2Calbum%2Creply_count&filter_id=%E6%90%9E%E7%AC%91%E8%90%8C%E5%AE%A0&start=24&_=1594378037461"
    xhr.open("GET", url)
    xhr.send()
    xhr.onreadystatechange = function () {
      if (xhr.readyState === 4 && /^2\d{2}/.test(xhr.status)) {
        // console.log(xhr.responseText)
        var data = JSON.parse(xhr.responseText)
        // console.log(data)
        var list = data.data.object_list
        console.log(list)
        var html = ""
        list.forEach(function (item, index) {
          // let { photo, msg } = item
          html += `
          <li class="lis">
           <h2>${item.msg}</h2>
           <img src="${item.photo.path}" alt="">
          </li>`
        })
        cont.innerHTML = html
      }
    }
  </script>
</body>

</html>

注意:本地服务器路径下打开html文件查看效果
在这里插入图片描述

感谢堆糖的支持,这里郑重承诺:仅做数据的获取,不做有损堆糖网站的操作。

同源策略是什么,跨域解决办法,cookie可以跨域吗?

Q:为什么会出现跨域问题?
A:出于浏览器的同源策略限制,浏览器会拒绝跨域请求。

注:严格的说,浏览器并不是拒绝所有的跨域请求,实际上拒绝的是跨域的读操作。浏览器的同源限制策略是这样执行的:

通常浏览器允许进行跨域写操作(Cross-origin writes),如链接,重定向;

通常浏览器允许跨域资源嵌入(Cross-origin embedding),如 img、script 标签;

通常浏览器不允许跨域读操作(Cross-origin reads)。

Q:什么情况才算作跨域?
A:非同源请求,均为跨域。名词解释:同源 —— 如果两个页面拥有相同的协议(protocol),端口(port)和主机(host),那么这两个页面就属于同一个源(origin)。
Q:为什么有跨域需求?

A:场景 —— 工程服务化后,不同职责的服务分散在不同的工程中,往往这些工程的域名是不同的,但一个需求可能需要对应到多个服务,这时便需要调用不同服务的接口,因此会出现跨域。

方法:JSONP,CORS,postmessage,webscoket,反向代理服务器等。

引用文章:https://juejin.im/post/6857800782276902919

cookie可以跨域吗?

正常情况下,浏览器禁止跨域获取cookie;
一般通过sso服务可以实现取得跨域cookie;

思路如下:

  • 域A页面访问位于域A的服务器,对权限进行验证
  • 域A服务器于域B服务器通信,记录一个唯一的加密串用作身份验证域(并将cookie信息发送给域B服务器)
  • 域A服务器返回302跳转,跳转到域B下,并将加密串作为url的一部分
  • 页面由域A跳转到域B,域B服务器通过加密串获取到事先从域A服务器上得到的cookie信息,并在响应头中添加set-cookie字段设置cookie
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值