关于javascript:详细讲解跨域问题相关概念及常见的CORS和JSONP解决方案代码

42次阅读

共计 5777 个字符,预计需要花费 15 分钟才能阅读完成。

什么是跨域问题

浏览器从一个域名的网页去申请另一个域名的资源时,域名、端口、协定任一不同,都是跨域

跨域跨域,见名知意,跨出畛域的意思。那什么是畛域呢?

咱们晓得,无论是前端还是后端代码想要运行,须要有一个服务撑持,也就是须要有一个 ip(主机)地址和对应的这个 ip(主机)上的端口。而这里的一个 ip 加端口,实际上就是指一个服务运行的中央,能够了解为 ip 加端口就是一个服务的畛域(当然也包含~http 或 https 也要保持一致,不然也会跨域)

而目前开发我的项目次要是前后端拆散做法,即前端本人启动一个前端服务畛域(前端本人有一个 ip 端口),后端本人启动一个后端服务畛域(后端也本人有本人的 ip 端口)须要什么数据资源,发个申请给后端,后端收到申请当前,从数据库中查问数据,失去后果再返回给前端。所以,此时就会呈现跨出畛域的问题~ 前端跨出本人的畛域到后端畛域要数据。而,在浏览器中有这样一条神圣法令:同源策略法令,同源策略规定发现申请跨域了,就会立即阻止这个申请,同时也会报错,揭示开发者。那么,什么是同源策略法令呢?

同源策略法令

所谓同源策源(Same-origin_policy),指的就是来源于同一个中央(域名、端口、协定要保持一致)的意思。这是一种十分重要的安全策略、计策。目前基本上所有的浏览器都遵循同源策略,同源策略规定:

1. 非同源的浏览器申请和相应,不能共享 Cookie、LocalStorage 和 IndexDB 等
2. 非同源,DOM 和 Js 对象无奈取得
3. 非同源,AJAX 申请被禁掉了

咱们能够看出同源策略还限度了不少操作的,无奈获取 Cookie、LocalStorage、DOM、Js 对象连 ajax 申请也被限度发送了,这样的话,确实是平安多了。特地是对于银行、金融机构。对于网络安全方面的常识(XSS、CSRF、SQL 注入攻打、Dos 攻打什么的)在这里就不赘述了,大家只须要记住,越严格的规定、越多条条框框,对于用户来说就越平安。所以给浏览器退出了同源策略限度。

然而同源策略平安是平安了,咱们开发我的项目就略有麻烦啊。呈现了跨域,ajax 申请被禁用了,这样的话,前后端接口没法联调了啊,所以咱们须要解决同源策略下的跨域问题

小历史:1995 年,Netscape 网景公司将同源策略规定引入浏览器,作为浏览器外围的平安性能,防备 XSS、CSFR 等攻打。

同源策略官网文档阐明:https://developer.mozilla.org…

出现跨域问题

在解决跨域问题之前,咱们先来出现一下跨域问题。看看跨域长什么样子的,这里的话,咱们就让前后端服务别离是不同的 ip 和端口,看一下

后端代码

后端用的电脑的 ip 是 10.9.26.107,端口是 4000,所以后端的域就是:http://10.9.26.107:4000/

const http = require('http');// 引入 http 模块

const port = 4000;// 设置一个端口

const server = http.createServer((req, res) => { // 创立一个服务返回数据
    res.statusCode = 200;
    res.setHeader("Content-type", "application/json");
    res.end('你好,跨域');
});

server.listen(port, () => { // 将服务启动在这个 4000 端口,并且监听
    console.log(`server running at ${port}/`);
});

前端代码

这里咱们应用 vue 框架,加上 axios 库,疾速高效些。至于前端的 ip 端口地址(域),这里用的是默认的本机端口:http://localhost:8080/

<template>
  <div id="box">
    <el-button @click="crossDomain"> 跨域浮现 </el-button>
  </div>
</template>

<script>
const axios = require('axios'); // 引入 axios 库
export default {
  methods: {crossDomain(){axios.get('http://10.9.26.107:4000/') // 向后端的服务发申请
      .then((res)=>{console.log('申请后果',res);
      })
      .catch((err)=>{console.log('报错',err);
      })
    }
  },
};
</script>

这里咱们的申请是:从 http://localhost:8080/ 域 到 http://10.9.26.107:4000/,ip 和端口都不一样,当然协定都是 http 是一样的。所以就会受到同源策略的限度,就会呈现跨域问题

跨域问题呈现

当咱们点击“跨域浮现”这个按钮的时候,呈现这样的报错,network 和控制台都有

翻译一下就是:
从本机的 localhost:8080 地位向 http://10.9.26.107:4000/ 发的 XMLHttp 申请被 CORS 政策禁止掉了,短少申请头设置 Access-Control-Allow-Origin

应用 CORS 解决跨域问题

CORS 简介

跨源资源共享(CORS)是 W3C 提出的解决跨域同源策略的一种计划(因为没有啥比拟好的计划,所以就新提出这个解决方案了),上述的案例中控制台也有提醒,就是应用 cors 去解决这个问题。只有设置一下,就能够解决跨域禁止拜访的问题了。因为 cors 计划简略易用,所以应用宽泛。

官网地址:https://developer.mozilla.org…

cors 解决跨域问题

若后端的拜访没什么限度的话,都容许拜访,只须要加上这句话,设置对应的响应头即可res.setHeader("Access-Control-Allow-Origin","*"); 因为星号 * 是代表所有的意思;如果想要指定某个网址,将星号换成对应网址即可。

要害代码如下:

const server = http.createServer((req, res) => {
    res.statusCode = 200;
    res.setHeader("Access-Control-Allow-Origin","*"); // 这句话示意容许所有的跨域申请, * 星号 指得是 所有的
    res.setHeader("Content-type", "application/json");
    res.end('你好,跨域');
});

当然 cors 写法多样,作用也很大,在理论开发中咱们能够有如下的写法:

 // 容许的 header 类型
 res.header("Access-Control-Allow-Headers","content-type");
 // 跨域容许的申请形式 
 res.header("Access-Control-Allow-Methods","DELETE,PUT,POST,GET,OPTIONS");
// 等等...

附上具体点的解说:https://www.w3cschool.cn/java…

CORS 计划略为简洁不便,所以篇幅不多,接下来咱们看看比拟古老的跨域解决方案:JSONP

应用 JSONP 解决跨域

JSONP 是什么

JSONP(JSON with Padding),是一个非官方的跨域解决方案,纯正凭借程序员的聪明才智做进去的,然而很局限,只反对 get 申请。post、put、delete 申请什么的,都不反对。平时工作中这种形式基本上不必,然而咱们还是要晓得的

JSONP 工作原理

咱们晓得在 html 的标签中,有一些标签在发明的时候,就容许跨域,就容许在某个地址上获取资源(比方图片资源、文件资源等)。比方 img 标签、link 标签、iframe 标签、script 标签

而 JSONP 就是应用 script 标签的跨域能力来发送申请的。

JSONP 写法举例

服务端代码

const express = require('express')
const app = express()
// 咱们应用 express 框架,指定一个端口,返回给前端数据
app.get('/myServer', (req, res) => {res.end("hello JSONP")
})
app.listen(6789, () => {console.log(` 服务曾经启动 `)
})

所以服务端代码的地址为:http://localhost:6789/myServer

前端代码

而前端的 html 代码中,咱们应用的是 vscode 编辑器,并且装置了 Live Server 这个插件,所以咱们右击通过启动浏览器,最终前端代码的域名端口是:http://127.0.0.1:5500/JSONP.html

Live Server插件默认是 5500 端口。比照一下前后端服务的域名端口,咱们发现,确实是跨域了,所以接下来咱们就看看 JSONP 是否可能解决跨域问题

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
</head>
<body>
    <!-- 应用 src 属性... -->
    <script src="http://localhost:6789/myServer"></script>
</body>
</html>

咱们用 script 标签的 src 属性指定服务端的地址,那么浏览器加载渲染页面的时候,就会向 src 中的地址发申请,获取数据。这个时候咱们关上 F12 中的 network 面板,咱们会发现申请确实是发送胜利了,也失去了数据,然而控制台却报错了。咱们看一下,上面两张图,图示如下:

跨域胜利申请图示


貌似很胜利的解决了跨域问题,然而实际上,跨域问题才解决一半,因为控制台报错了,如图:

跨域胜利控制台报错图示

JSONP 报错起因

JSONP 不能间接返回数据,间接返回数据浏览器辨认不了,就会报语法错,报错信息上图。因为浏览器的 script 标签须要是 js 代码,既然要 js 代码,咱们就想到给浏览器一个函数 js 代码,把须要返回给前端的数据,作为函数中的参数传递给前端,这样的话,浏览器就能辨认了。而前端只须要申明一个对应的函数接管就行了
换而言之,JSOP 须要将数据作为函数的参数返回,而不是间接返回数据,对应的,前端须要申明一个雷同名字的函数,用于接管这个数据
所以正确的写法是这样的

JSONP 正确写法

服务端

app.get('/myServer', (req, res) => {
    let data = {
        name:"孙悟空",
        age:"500",
        home:"花果山水帘洞"
    }
    let resData = JSON.stringify(data)
    res.end(`fn(${resData})`)
})

客户端

<body>
    <script>
        // 这个函数名字 fn 必须和后端定义的名字保持一致,这样在解析的时候能力
        // 对应上,能力找到相应的函数,能力失去相应的数据
        function fn(data) {console.log(data);
        }
    </script>
    <script src="http://localhost:6789/myServer"></script>
</body>

JSONP 前端残缺写法

假如咱们是点击按钮 JSONP 发申请,咱们看一下原生形式代码如下:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
</head>
<body>
    <button> 发申请 </button>
    <script>
        // 第一步,申明一个函数用于接管后端返回的
        function fn(data) {console.log('后端返回的数据', data);
        }
        document.querySelector('button').onclick = function () {
            // 第二步,创立一个 script 标签
            let script = document.createElement("script")
            // 第三步,指定发申请的地址
            script.src = "http://localhost:6789/myServer"
            // 第四步,将标签插入到文档中
            document.body.appendChild(script)
        }
    </script>
</body>
</html>

所以上述就是一个简略的应用 JSONP 的小案例,然而,JSONP 只反对 GET 申请,纵然 JSONP 的劣势在于反对老式浏览器,以及能够向不反对 CORS 的网站申请数据。实际上咱们想想,当初曾经是 2021 年了,基本上老式浏览器都被淘汰了。所以 JSONP 这个基本上不必,然而咱们也要晓得这个货色

文章总结

  • CORS 反对所有类型的 HTTP 申请,是跨域的最好的解决方案(需后端配置)
  • JSONP 仅反对 GET 申请,JSONP 的劣势在于作用在一些古老的浏览器,毕竟古老的浏览器是没有 CORS 规定的(前端书写、后端配合定义函数名)说实话,根本不必,晓得即可

还能够通过 Node 直达代理的形式,比方 vue 框架中的 vue.config.js 文件外面的 devServer 中的 proxy 对象做配置

当然也能够通过 nginx 反向代理解决跨域问题(前期写 nginx 文章的时候,会专门介绍相干 nginx 反向代理的配置)

不论是 Node 中间件代理还是 nginx 反向代理,次要是通过同源策略不作用于服务器来的管制的,毕竟同源策略是浏览器外面的规定,和服务器没有关系。日常工作中,支流跨域解决方案是 cors 和 nginx 反向代理

正文完
 0