同源策略产生介绍及解决跨域最常见的三种方式(JSONP+CORS+反向服务器代理),用详细的前后端分离代码带你搞懂跨域原理

        什么是同源及出现原因

        JSONP解决跨域

        CORS(后端设置允许跨域实现)

        反向代理(proxy,配置代理服务器)


什么是同源及出现原因

        我们知道,在搜索引擎还没有流行的年代,也就是计算机刚起步的时候,那时如果想要访问一个网站,就必须要将它的地址完整的输入上去发送http请求才会呈现网页内容。如果访问的某个网站需要你充值才能够去访问,肯定有让你输入账号密码的时候吧。

        比如你刚刚登录过一个一个银行网站,网址是http://www.xxx.com,你登录后的账号和密码是存放在浏览器cookie中的,如果你向银行发送的请求成功了,那么这段xxx.com网址便有办法获取到你的账号和密码。所以浏览器便为了用户安全出台了同源策略,也就是你的请求可以发出去,但是却不让你拿到响应内容,也就是形成了这个域不能去访问其他域的功能。

        这里的域指的是协议(http://)、主机(www.xxx.com)、端口号(:8080)

        也就是http://www.xxx.com:8080,必须要求三者与你去的域名相对应才能够进行访问,

        比如 aaa.bbb.com 和 ccc.bbb.com 它们都属于 bbb.com 这个域名的,但他们并不属于同源,现在要让它们两个进行交互,那么就需要使用跨域技术了。         

JSONP解决跨域

        从另一个角度来看待跨域的问题,其实就是用AJAX进行请求数据时会遇到的访问问题,浏览器会给你报错,Fetch也是有这样的问题。这时候你可能会问,为什么在script标签中引用了别的源却不会出现这样的问题呢?这种情况相信写过JS代码的都知道吧。这是因为JS设计人员当初在设计<script>标签的时候就允许了在别的源请求脚本,所以就有很聪明的开发前辈利用这个 “ 漏洞 ” 进行跨域,这个方法便是JSONP。 

下面来看一下script标签访问别的源的原理

<script 
    src="http://localhost:8000/server/index/php" // 访问php文件
    type="text/javascript">
</script>
<?php
    echo " console.log('做一名合格且优秀的程序猿'); "; // php代码中的返回
?>

当浏览器打开php文件时可以看到echo后面的一段字符串被输出出来了

 当浏览器打开html文件时可以看到已经解析并执行了php文件中返回的字符串

这便是script标签的一个基本功能,还有一个可忽视的重要地方,就是H5中script标签默认的 type="text/javascript" 代码,正是定义了此类型,所以请求的内容才会被浏览器解析执行,在上面的 PHP 代码就是被浏览器以JS代码形式来执行,这便为跨域提供了可能性。 

那么现在我们改一下代码

<script>
    function param(data){
        console.log(data);
    }
</script>
<script 
    src="http://localhost:8000/server/index.php" // 访问php文件
    type="text/javascript">
</script>
<?php
    echo " param('做一名合格且优秀的程序猿'); ";
?>

        结果还是打印 ' 做一名合格且优秀的程序猿 ',道理和刚才一样,浏览器解析了PHP中的 param 作为函数的调用并传递了里面的参数。

        那么通过这个原理我们就可以进行跨域访问了。

        具体要怎么做呢?先回顾一个URL小知识,相信很多人都知道URL是由协议、主机、端口号、路径及额外参数组成的,那么JSONP跨域便是用到了这其中的额外参数

        从 ? 开始到后面的一系列参数便是客户端提供给服务器的额外参数,用于表面你要请求那些据,比如http://www.xxx.com:8080/anime/?key=value。key=value便是额外参数,这些额外参数以键值对形式定义,& 符进行分割,当服务器看到这些请求参数,且已经提前设置好了回应代码,就会使用这些参数来执行额外操作,我们便可以利用这点来获取服务器的数据了。

        客户端与服务端常见的交互格式就是JSON格式了,所以说我们提前在客户端封装好一个函数,这个函数专门处理获取获取服务器JSON格式的数据,然后在URL上提供给服务器的额外参数中携带这个函数。服务器看的这些额外代码,会把数据外包一个JS函数再以JSON数据的形式传回客户端,当客户端收到后,调用提前封装好的函数执行服务器传过来的数据,也就是执行函数并且获取数据。简单来说就是不直接获取服务器数据,而是给数据包裹JS函数,变成不一样的JSON数据,再去到客户端那边就顺利成章了

因为一般是用JS函数包裹JSON数据,所以称之为 JSON with Padding,JSONP

那么就通过代码看以下它的实现过程吧

<script>
    function param(data){
        console.log(data);
    }
</script>
<script 
     
    src="http://localhost:8000/server/index.php?callback=param" /* 将param函数作为参数传递给服务器 */
    type="text/javascript">
</script>
<?php
    header("Content-type:application/json"); // 设置http头部 - 内容类型(服务器要返回json格式)
    $callbackGET = $_GET["callback"]; // 收集客户端发来的值(收集了传递过来的callback)函数
    $data = "做一名合格且优秀的程序猿"; // 创建$data变量,并传入字符串
    eaco $callbackGET."("'.$data.'")" // 使用eaco返回$callbackGET变量并与data拼接,实际返回param函数并将服务端数据传递过去执行
?>

        实际在客户端调用param函数,返回的数据依旧是" 做一名合格且优秀的程序猿 ",这便是JSONP的核心原理了,这个拿JQuery封装好的直接使用更好。JSONP有个很大的缺点便是只能在GET请求中使用,不支持POST,所以诞生了CORS方法。

 CORS(后端设置允许跨域实现)

        当浏览器在进行跨域请求时,会在请求中添加头部origin来表明自己的协议、主机、端口号,当服务器接到请求并且看到这个origin头部时,如果设置过允许此域名进行访问,就得添加头部Access-Control-Allow-Origin到响应里面,浏览器看到服务器传回来的这个头部就知道能不能进行跨域请求了。

下面我们使用node.js来完成,首先需要npm install cors安装,在服务器端提前配置好请求头信息

<script>
    fetch( "http://127.0.0.1:35911" )
    .then( response => response.json() )
    .then( data => console.log(data.like) ) 
</script>
const express = require("express");
const cors = require("cors"); // 引入cors
const app = express();
app.use(cors( // 使用cors并添加 origin 头部信息
    {
        origin: "*", // 设置允许哪些源类型来拿资源(比如http://127.0.0.1:35911:8080),协议主机端口都需要写,* 标识允许所有源
    })
);
app.get("/", (request, response) => {
    response.json(
        {"name":"不读诗意", "like":"没人看我的文章"} // 服务端返回的JSON数据
    );
})
app.listen(35911);    

此时在Networt里查看响应头部也能够找到信息了。

给大家扩展一下,如果有人恶意使用DELETE添加在Fetch中,想要删除服务器的数据的话,比如

fetch( "http://127.0.0.1:35911",mothod:"delete")

结果却是这样的,说你的请求为OPTIONS类型并不是DELETE

        那么浏览器也会防一手的,当用户想修改服务器数据时,例如使用PUT、PATCH或者DELETE,浏览器会自动发出有个预检请求,预检请求就是查看服务器是否支持当前跨域请求,因此预检请求没有具体哪个方法,而是OPTIONS,此时下面也会显示服务器允许的方法,知道允许后才会去实际请求,所以服务端那边也会提前设置好允许跨域请求的方式。

app.use(cors( // 使用cors并添加 origin 头部信息
    {
        origin: "*", 
        methods: ["GET","POST"] // 设置允许服务器请求时使用的方法,通常是GET和POST
    })
);

这个时候客户端只要以正确的方式访问便会不出现错误。

CORS的核心便是设置头部 Access-Control-Allow-Origin,方法比JSONP简单多了,不过需要主页兼容的问题。

反向代理(proxy,配置代理服务器)

        最后一种常见解决跨域的方法就是设置反向代理了,也就是前端自己配置一个和自己同源的服务器代替自己去请求向后台数据然后返还给自己,因为同源策略限制的是浏览器与服务器之间,而服务器与服务器之间是没有同源策略的限制的。

        vue的使用便是在vue.config.js文件中配置以下信息:

 后面使用 http://localhost:7777接口直接用 /api 进行代理即可。


总结:

        同源策略:浏览器为了用户安全出台了同源策略

        当请求接口访问的服务器域名与进行访问的浏览器的域名有协议(http://)、主机(www.xxx.com)、端口号(:8080)三者有一个不匹配时就会违反同源策略产生跨域。

        跨域的三种解决方式:

        JSONP,运用<script>标签允许在别的源请求脚本的原理,在请求时将封装好的函数作为参数拼接在URL中,后端收到请求后,调用该函数,并将准备好的JSON数据作为参数返回出去。(仅支持GET请求)

        CORS(后端设置允许跨域实现,最常用) ,浏览器进行跨域请求时,在请求中添加头部origin表明自己的协议、主机、端口号,服务器接到请求头部查看是否设置过允许此域名进行访问,允许的话添加头部Access-Control-Allow-Origin到响应里面,浏览器看到服务器传回来的这个头部就知道能不能进行跨域请求了。

        前端自己配置一个和自己同源的服务器代替自己去请求向后台数据然后返还给自己,因为同源策略限制的是浏览器与服务器之间,而服务器与服务器之间没有同源策略的限制。

        希望能够有所帮助!

        

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值