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


prtyaa
prtyaa 2023-12-26 18:41:10 60643
分类专栏: 资讯

什么是跨域问题

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

跨域跨域,见名知意,跨出领域的意思。那什么是领域呢?
我们知道,无论是前端还是后端代码想要运行,需要有一个服务支撑,也就是需要有一个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等攻击。
同源策略官网文档说明:

呈现跨域问题

在解决跨域问题之前,我们先来呈现一下跨域问题。看看跨域长什么样子的,这里的话,我们就让前后端服务分别是不同的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方案简单易用,所以使用广泛

官方地址:

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");
// 等等...
附上详细点的讲解: w3cschool.cn/javascript

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反向代理

网站声明:如果转载,请联系本站管理员。否则一切后果自行承担。

本文链接:https://www.xckfsq.com/news/show.html?id=31032
赞同 0
评论 0 条
prtyaaL0
粉丝 1 发表 2554 + 关注 私信
上周热门
银河麒麟添加网络打印机时,出现“client-error-not-possible”错误提示  1448
银河麒麟打印带有图像的文档时出错  1365
银河麒麟添加打印机时,出现“server-error-internal-error”  1151
统信桌面专业版【如何查询系统安装时间】  1073
统信操作系统各版本介绍  1070
统信桌面专业版【全盘安装UOS系统】介绍  1028
麒麟系统也能完整体验微信啦!  984
统信【启动盘制作工具】使用介绍  627
统信桌面专业版【一个U盘做多个系统启动盘】的方法  575
信刻全自动档案蓝光光盘检测一体机  484
本周热议
我的信创开放社区兼职赚钱历程 40
今天你签到了吗? 27
信创开放社区邀请他人注册的具体步骤如下 15
如何玩转信创开放社区—从小白进阶到专家 15
方德桌面操作系统 14
我有15积分有什么用? 13
用抖音玩法闯信创开放社区——用平台宣传企业产品服务 13
如何让你先人一步获得悬赏问题信息?(创作者必看) 12
2024中国信创产业发展大会暨中国信息科技创新与应用博览会 9
中央国家机关政府采购中心:应当将CPU、操作系统符合安全可靠测评要求纳入采购需求 8

添加我为好友,拉您入交流群!

请使用微信扫一扫!