跨域&解决方案(CORS,JSONP)


(参考视频:B站禹神)


一、跨域&同源策略

指的是 在浏览器中发送的请求从一个域名(或端口、协议)发送到另一个不容的域名(或端口、协议)。这是因为在浏览器中有一个在安全策略叫做**“同源策略”**,它限制了来自不同的源的“写”访问,但允许来自相同源的“读”访问。

具体来说,同源策略要求以下三个组成部分必须完全相同:

  • 协议(http或https)
  • 域名
  • 端口号

二、浏览器会对跨域做出哪些限制?

例:[源A] 和 [源B] ,他们是非同源的,则浏览器会有如下限制:

  • DOM访问限制:[源A] 的脚本不能读取和操作 [源B] 的DOM。
  • Cookie访问限制:[源A] 不能访问 [源B] 的cookie。
  • Ajax响应数据限制:[源A] 可以给 [源B] 发请求,但是无法获得 [源B] 的响应。

备注:在上述限制中,浏览器对Ajax获取数据的限制是影响最大的一个,且实际开发中经常遇到。

三、注意点

  1. 跨域限制仅存在于浏览器端,服务器端不存在跨域限制。
  2. 即便跨域了,Ajax请求也可以正常发出,但响应数据不会交给开发者。

四、CORS解决Ajax跨域问题

4.1 CORS概述

CORS(Cross-Origin Resource Sharing,跨源资源共享)是一种机制,它使用额外的 HTTP 头来告诉浏览器允许一个域上的网页访问另一个域上的资源。
注:使用CORS解决跨域问题是最正统的方式,且要求服务器是“自己人”。

4.2 CORS解决简单请求跨域

简单请求.html

<!DOCTYPE html>
<html lang="zh-CN">
<head>
  <meta charset="UTF-8">
  <title>4-解决跨域问题——简单请求</title>
</head>
<body>
  <button onclick="getStudents()">获取学生列表</button>
  
  <script type="text/javascript">
    async function getStudents() {
      console.log('发送请求了')
      const url= 'http://127.0.0.1:8081/students';
      let result = await fetch(url,{
        mode:'cors'
      })
      let data = await result.json()
      console.log(data)
    }
  </script>
</body>
</html>

server.js

const express = require('express')
const app = express()

const students = [
  { id: 1, name: '张三', age: 18, gender: '男' },   
  { id: 2, name: '李四', age: 19, gender: '女' },
]

//定义一个路由处理器
app.get('/students', (req, res) => {
  res.setHeader('Access-Control-Allow-Origin','http://127.0.0.1:5500')
  res.send(students)
})

//启动服务器
app.listen(8081,()=>{
  console.log('Example app listening at http://localhost:8081')
})

4.3 简单请求和复杂请求

简单请求复杂请求
请求方法GET、HEAD 或 POSTPUT、DELETE、CONNECT、OPTIONS、TRACE 或任何其他非简单请求方法。
请求头1.请求头中不能包含任何自定义的请求头,只能包含某些预定义的简单头部。
2.如果请求方法是 POST,那么 Content-Type 必须是
  • application/x-www-form-urlencoded
    • 或multipart/form-data
      • 或 text/plain。
请求包含自定义请求头(即不是预定义的简单头部).
特点浏览器会在响应中检查 Access-Control-Allow-Origin 头,以确定是否允许该跨域请求复杂请求会自动发送预检请求

关于预检请求:

  1. 发送时机:预检请求在实际跨域请求之前发出,是由浏览器自动发起的。
  2. 主要作用:用于向服务器确认是否允许接下来的跨域请求。
  3. 基本流程:先发起OPTIONS请求,如果通过预检,继续发起实际的跨域请求。
  4. 请求头内容:一个OPTIONS预检请求,通常会包含如下请求头:
请求头含义
Access-Control-Allow-Origin指定哪些源可以访问资源。
Access-Control-Allow-Methods列出允许的方法。
Access-Control-Allow-Headers如果预检请求中指定了特定的头部,那么这个头部会列出实际请求中允许使用的头部字段。
Access-Control-Max-Age可选,用来告知浏览器预检请求的结果可以被缓存多久。

4.4 CORS处理复杂请求

复杂请求.html

<!DOCTYPE html>
<html lang="zh-CN">
<head>
  <meta charset="UTF-8">
  <title>4-解决跨域问题——简单请求</title>
</head>
<body>
  <button onclick="getStudents()">获取学生列表</button>
  
  <script type="text/javascript">
    async function getStudents() {
      console.log('发送请求了')
      const url= 'http://127.0.0.1:8081/students';
      let result = await fetch(url,{
        mode:'cors',
        method:'GET',
        headers:{
          school:'pecking',
          city:'beijing'
        }
      })
      let data = await result.json()
      console.log(data)
    }
  </script>
</body>
</html>

server,js

在这里插入代码片const express = require('express')
const app = express()

const students = [
  { id: 1, name: '张三', age: 18, gender: '男' },   
  { id: 2, name: '李四', age: 19, gender: '女' },
]
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,city')
  res.setHeader('Access-Control-Allow-Max-Age',7200)
  res.send()
})
//定义一个路由处理器
app.get('/students', (req, res) => {
  res.setHeader('Access-Control-Allow-Origin','http://127.0.0.1:5500')
  res.send(students)
})

//启动服务器
app.listen(8081,()=>{
  console.log('Example app listening at http://localhost:8081')
})

4.5 借助cors库快速完成配置

1.安装库

npm i cors

2.导入

const cors = require(‘cors’)

3.使用中间件

 app.use(cors({
>   origin: 'xxx',	//允许的源
>  	methoda: ['GET', 'POST', 'PUT', 'DELETE', 'HEAD', 'OPTIONS'],	//允许的方法
>  	allowedHeaders: ['xxx']
>   }))		//允许的自定义头
>  	exposedHeaders: ['xxx']	//要暴露的请求头
>  optionSuccessStatus: 200	//预检请求成功的状态码

五、JSONP解决跨域问题

JSONP 的工作原理
  1. 客户端发送请求:
    客户端通过 <script> 标签向服务器发起请求。
    由于 <script> 标签不受同源策略的限制,可以加载任何来源的脚本文件,因此可以用来向跨域服务器请求数据。
  2. 服务器响应:
    服务器接收到请求后,将数据包装在一个函数调用中返回给客户端。
    这个函数通常是客户端在发起请求时指定的一个回调函数名。
  3. 客户端执行回调函数:
    当脚本加载完成后,浏览器会执行该脚本,从而执行回调函数,并将数据作为参数传递给这个函数。这样,客户端就可以处理这些数据了。
应用代码示例:

JSONP.html

<!DOCTYPE html>
<html lang="zh-CN">
<head>
  <meta charset="UTF-8">
  <title>JSONP</title>
</head>
<body>
  <button onclick = "getStudents()">获取学生数据</button>
  <script type="text/javascript">
    function test(data){
      console.log('callback被调用了',data)
    }
    function getStudents(){
      const script = document.createElement('script')
      script.onload = ()=>{
        script.remove()
      }
      script.src = 'http://127.0.0.1:8081/students?callback=test'
      document.body.appendChild(script)
    }
  </script>
  <!-- <script>
    function callback(data){
      console.log('callback被调用了',data)
    }
  </script>
  <script type="text/javascript" src="http://127.0.0.1:8081/students"></script>
 -->
</body>
</html>

Server.js

const express = require('express')
const app = express()
const cors = require('cors')


const students = [
  { id: 1, name: '张三', age: 18, gender: '男' },   
  { id: 2, name: '李四', age: 19, gender: '女' },
]


app.use(cors())
//定义一个路由处理器
app.get('/students', (req, res) => {
  const { callback } = req.query
  res.send(`${callback}(${JSON.stringify(students)})`)
})

//启动服务器
app.listen(8081,()=>{
  console.log('Example app listening at http://localhost:8081')
})

六、配置代理解决跨域(以下内容未具体阐述)

6.1 自己配置代理服务器

6.2 使用Nginx搭建代理服务器

6.3 借助脚手架搭建代理服务器

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值