CORS跨域问题梳理
什么是跨域#
浏览器的同源策略:浏览器为确保资源安全,而遵循的一种策略,该策略对访问资源进行了一些限制
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的cookie
console.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的封装)
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步