浏览器从一个域名的网页去请求另一个域名的资源时,域名、端口、协议任一不同,都是跨域
跨域跨域,见名知意,跨出领域的意思。那什么是领域呢?
我们知道,无论是前端还是后端代码想要运行,需要有一个服务支撑,也就是需要有一个ip(主机)地址和对应的这个ip(主机)上的端口。而这里的一个ip加端口,实际上就是指一个服务运行的地方,可以理解为ip加端口就是一个服务的领域(当然也包括~http或https也要保持一致,不然也会跨域)
而目前开发项目主要是前后端分离做法,即前端自己启动一个前端服务领域(前端自己有一个ip端口),后端自己启动一个后端服务领域(后端也自己有自己的ip端口)需要什么数据资源,发个请求给后端,后端收到请求以后,从数据库中查询数据,得到结果再返回给前端。所以,此时就会出现跨出领域的问题~前端跨出自己的领域到后端领域要数据。而,在浏览器中有这样一条神圣法则:同源策略法则,同源策略规则发现请求跨域了,就会立刻阻止这个请求,同时也会报错,提醒开发者。那么,什么是同源策略法则呢?
所谓同源策源(Same-origin_policy),指的就是来源于同一个地方(域名、端口、协议要保持一致)的意思。这是一种非常重要的安全策略、计谋。目前基本上所有的浏览器都遵循同源策略,同源策略规定:
我们可以看出同源策略还限制了不少操作的,无法获取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)是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");
// 等等...
附上详细点的讲解: https://www.w3cschool.cn/javascript_guide/javascript_guide-z4jy26a3.html
CORS方案略为简洁方便,所以篇幅不多,接下来我们看看比较古老的跨域解决方案: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这个基本上不用,但是我们也要知道这个东西
还可以通过Node中转代理的方式,比如vue框架中的vue.config.js文件里面的devServer中的proxy对象做配置
当然也可以通过nginx反向代理解决跨域问题(后期写nginx文章的时候,会专门介绍相关nginx反向代理的配置)
不管是Node中间件代理还是nginx反向代理,主要是通过同源策略不作用于服务器来的控制的,毕竟同源策略是浏览器里面的规则,和服务器没有关系。日常工作中,主流跨域解决方案是cors和nginx反向代理
网站声明:如果转载,请联系本站管理员。否则一切后果自行承担。
添加我为好友,拉您入交流群!
请使用微信扫一扫!