什么是跨域
浏览器的同源策略:浏览器为确保资源安全,而遵循的一种策略,该策略对访问资源进行了一些限制
https://www.w3.org/Security/wiki/Same_Origin_Policy
https://developer.mozilla.org/zh-CN/docs/Web/HTTP/CORS
1、发生跨域后会出现的问题:
1、限制DOM访问
<!-- <iframe id="framePage" src="./demo.html"></iframe> -->
<iframe id="framePage" src="https://www.baidu.com"></iframe><script type="text/javascript" >function showDOM(){const framePage = document.getElementById('framePage')console.log(framePage.contentWindow.document) //同源的可以获取,非同源的无法获取}
</script>
2、限制cookie访问(实际上dom无法访问,cookie也自然无法访问了)
<iframe id="baidu" src="http://www.baidu.com" width="500" height="300"></iframe><script type="text/javascript" >// 访问的是当前源的cookie,并不是baidu的cookieconsole.log(document.cookie)
</script>
3、限制Ajax获取数据(请求可以发出,但是无法获取源B的响应数据)
const url = 'https://www.toutiao.com/hot-event/hot-board/?origin=toutiao_pc'
let result = await fetch(url)
let data = await result.json();
console.log(data)
2、注意点
1、跨域限制仅存在浏览器端,服务端不存在跨域限制
2、即使跨域了,Ajax 请求也可以正常发出,但响应数据不会交给开发者
3、<link>
、<script>、
...... 这些标签发出的请求也可能跨域,只不过浏览器对标签跨域不做严格限制,对开发几乎无影响
跨域的解决方案
1、解决方案一:CORS
CORS 全称:Cross-Origin Resource Sharing(跨域资源共享),是用于控制浏览器校验跨域请求的一套规范,服务器依照 CORS 规范,添加特定响应头来控制浏览器校验,大致规则如下:
● 服务器明确表示拒绝跨域请求,或没有表示,则浏览器校验不通过。
● 服务器明确表示允许跨域请求,则浏览器校验通过
(1)处理简单请求
简单请求
- 请求方法为:GET、HEAD、POST
- 请求头的Content-Type的值只能是以下三种:
● text/plain
● multipart/form-data
● application/x-www-form-urlencoded
服务器响应时候,添加Access-Control-Allow-Origin
响应头,声明允许某个源发起跨域请求,浏览器校验通过
服务端核心代码(以express框架为例)
// 处理跨域中间件
function corsMiddleWare(req,res,next){// 允许 http://127.0.0.1:5500 这个源发起跨域请求// res.setHeader('Access-Control-Allow-Origin','http://127.0.0.1:5500')// 允许所有源发起跨域请求res.setHeader('Access-Control-Allow-Origin','*')next()
}// 配置路由并使用中间件
app.get('/',corsMiddleWare,(req,res)=>{res.send('hello!')
})
(2)处理复杂请求
复杂请求:不是简单请求的请求就是复杂请求,比如application/json
复杂请求会自动发送预检请求
解决方案
第一步:服务器先通过浏览器的预检请求,服务器需要返回如下响应头:
第二步:处理实际的跨域请求(与处理简单请求跨域的方式相同)
服务端核心代码
// 处理预检请求
app.options('/students', (req, res) => {// 设置允许的跨域请求源res.setHeader('Access-Control-Allow-Origin', 'http://127.0.0.1:5500')// 设置允许的请求方法res.setHeader('Access-Control-Allow-Methods', 'GET')// 设置允许的请求头res.setHeader('Access-Control-Allow-Headers', 'school')// 设置预检请求的缓存时间(可选)res.setHeader('Access-Control-Max-Age', 7200)// 发送响应res.send()
})// 处理实际请求
app.get('/students', (req, res) => {// 设置允许的跨域请求源res.setHeader('Access-Control-Allow-Origin', 'http://127.0.0.1:5500')// 随便设置一个自定义响应头res.setHeader('abc',123)// 设置允许暴露给客户端的响应头res.setHeader('Access-Control-Expose-Headers', 'abc')// 打印请求日志console.log('有人请求/students了')// 发送响应数据res.send(students)
})
2、解决方案二:使用cors库等
nodejs
// 配置cors库
app.use(cors())
// cors中间件配置
const corsOptions = {origin: 'http://127.0.0.1:5500', // 允许的源methods: ['GET', 'POST', 'PUT', 'DELETE', 'HEAD', 'OPTIONS'], // 允许的方法allowedHeaders: ['school'], // 允许的自定义头exposedHeaders: ['abc'], // 要暴露的响应头optionsSuccessStatus: 200 // 预检请求成功的状态码
};app.use(cors(corsOptions)); // 使用cors中间件
django
INSTALLED_APPS = ['django.contrib.admin',...'django.contrib.messages','django.contrib.staticfiles',# 处理跨域'corsheaders',
]MIDDLEWARE = [...'django.contrib.sessions.middleware.SessionMiddleware',# 处理跨域"corsheaders.middleware.CorsMiddleware",...
]
3、解决方案三、JSONP
- JSONP 概述: JSONP 是利用了
<script>
标签可以跨域加载脚本,且不受严格限制的特性,可以说是程序员智慧的结晶,早期一些浏览器不支持 CORS 的时,可以靠 JSONP 解决跨域。 - 基本流程:
○ 第一步:客户端创建一个<script>
标签,并将其src属性设置为包含跨域请求的 URL,同时准备一个回调函数,这个回调函数用于处理返回的数据。
○ 第二步:服务端接收到请求后,将数据封装在回调函数中并返回。
○ 第三步:客户端的回调函数被调用,数据以参数的形势传入回调函数。
JavaScript核心代码
<button onclick="getTeachers()">获取数据</button><script type="text/javascript" >function callback(data){console.log(data)}function getTeachers(url){// 创建script元素const script = document.createElement('script')// 指定script的src属性script.src= 'http://127.0.0.1:8081/teachers'// 将script元素添加到body中触发脚本加载document.body.appendChild(script)// script标签加载完毕后移除该标签script.onload = ()=>{script.remove()}}
</script>
jQuery 封装的 jsonp
$.getJSON('http://127.0.0.1:8081/teachers?callback=?',(data)=>{console.log(data)
})
4、配置代理解决跨域
4.1 自己配置代理服务器
借助http-proxy-middleware配置代理
const { createProxyMiddleware } = require('http-proxy-middleware');app.use('/api',createProxyMiddleware({target:'https://www.toutiao.com',changeOrigin:true,pathRewrite:{'^/api':''}
}))
4.2
基于nginx搭建代理服务器,基于Vue等脚手架搭建代理服务器(本质上是对4.1的封装)