跨域问题分析以及解决方案

1 浏览器的同源策略

  • 浏览器为确保资源安全,而遵循的一种策略。

2 什么是源

  • 源 = 协议 + 域名 + 端口号
  • 同源与非同源
    • 源1源2是否同源
      http://www.xyz.com/homehtts://www.xyz.com/home非同源
      http://www.xyz.com/homehttp://mail.xyz.com/home非同源
      http://www.xyz.com:8080/homehttp://www.xyz.com:8090/home非同源
      http://www.xyz.com:8080/homehttp://www.xyz.com:8080/search同源
  • 所处源与目标源不一致,就是非同源,又称跨域。

3 浏览器对跨域访问的限制

  • 如果源A和源B,是非同源的,浏览器会有如下限制
  • DOM访问限制: 源A的脚本不能读取和操作源B的DOM
  • Cookue访问限制:源A不能访问源B的cookie
  • Ajax响应数据限制:源A可以给源B发请求,但是无法获取源B响应的数据
  • 浏览器对Aiax获取数据的限制是影响最大的一个,且实际开发中经常遇到
  • 跨域限制仅存在浏览器端,服务端不存在跨域限制

4 Ajax响应数据限制演示

  • 示意图

    • 请添加图片描述
  • 这里借助nginx演示下

    • 修改index.html内容如下
      •   <!DOCTYPE html>
          <html>
              <head>
                  <meta charset="UTF-8">
                  <title>跨域访问</title>
              </head>
              <body>
                  <button onclick="getNews()">获取新闻</button>
                  <script type="text/javascript">
                      async function getNews(){
                          const myurl = "https://www.toutiao.com/hot-event/hot-board/?origin=toutiao_pc"
                          let result = await fetch(myurl)
                          let data = await result.json()
                          console.log(data)
                      }
                  </script>
              </body>
          </html>
        
  • 在谷歌浏览器中访问,可以看到报错了

    • 在这里插入图片描述
  • 我们所处的源为 http://192.168.206.146,访问的目标源 https://www.toutiao.com,为跨域访问,因此无法获取响应数据。

5 CORS解决Ajax跨域问题

CORS简介

  • CORS全称:Cross-Origin Resource Sharing(跨域资源共享),是用于控制浏览器校验跨域请求的一套规范,服务器依照CORS规范,添加特定响应头来控制浏览器校验,规则如下
    • 服务器明确表示拒绝跨域请求,或没有表示,则浏览器校验不通过
    • 服务器明确表示允许跨域请求,则浏览器校验通过

简单请求与复杂请求

  • CORS会把请求分为两类:简单请求和复杂请求。
  • 简单请求
    • 请求方法为GET、POST、HEAD
    • 请求头字段符合《CORS安全规范》。只要不手动修改请求头,一般都能符合该规范
    • 请求头的 Content-Type只能是以下三种
      • text/plain
      • multipart/form-data
      • application/x-www-form-urlencoded
  • 复杂请求
    • 不符合简单请求的要求,就都是复杂请求
    • 复杂请求会自动发送预检请求
  • 预检请求
    • 发送时机:预检请求在实际跨域请求之前发出,由浏览器发起。
    • 主要作用:用于向服务器确认是否允许接下来的跨域请求。
    • 基本流程:先发起OPTIONS请求,如果通过预检,继续发起实际的跨域请求。
    • 请求头内容:一个OPTIONS预检请求,通常会包含如下请求头
      • Origin: 发起请求的源
      • Access-Control-Request-Method: 实际请求的HTTP方法
      • Access-Control-Request-Headers: 实际请求中使用的自定义头

CORS解决简单请求的跨域问题

  • 示意图
    • 请添加图片描述
  • 浏览器请求时,请求头都会带一个Origin,表示源地址。可以在服务端的响应头中加一个 Access-Control-Allow-Origin字段,表示允许哪个源访问,填写允许访问的源地址,就可以解决跨域问题。

CORS解决复杂请求的跨域问题

  • 第一步:服务器先通过浏览器的预检请求,返回如下响应头
    • Access-Control-Allow-Origin: http://192.168.206.146:80
    • Access-Control-Allow-Methods: GET
    • Access-Control-Allow-Headers: myname
    • Access-Control-Max-Age: 7200
    • 备注: Access-Control-Allow-Headers是自定义的响应头,Access-Control-Max-Age是可选的,缓存时间,表示多长时间内不需要再发送预检请求。
  • 示意图
    • 请添加图片描述
  • 第二步:处理实际的跨域请求(和处理简单请求的跨域问题方法一样)

通过cors解决跨域问题

  • 上面演示的方法,手动修改比较麻烦,可以通过cors去配置。
  • 安装cors后,在服务端的js代码中写入以下内容
  •   const cors = require('cors')
      app.use(cors({
      	origin: 'http://192.168.206.146:80', // 允许的源
      	methods: ['GET', 'POST','OPTIONS'],  // 允许的方法
      	allowedHeaders: ['myname']           //允许的自定义头
      }))
    

6 JSONP解决Ajax跨域问题

JSONP概述

  • JSONP概述:JSONP是利用了 <script> 标签可以跨域加载脚本,且不受严格限制的特性去解决跨域问题。但JSONP有局限性,只能解决GET请求的跨域问题。

原理

  • 基本流程
    • 第一步:客户端创建一个 <script> 标签,并将其src属性设置为包含跨域请求的URL,同时准备一个回调函数,这个回调函数用于处理返回的数据。
    • 第二步:服务端接收到请求后,将数据封装在回调函数中并返回。
    • 第三步:客户端的回调函数被调用,数据以参数的形式传入回调函数。
  • 示意图
    • 请添加图片描述

代码实现

  • server端代码
    •   const express = require('express')
        const app = express()
        
        const teachers = [
        	{id:'001', name:'jack', age:18},
        	{id:'002', name:'lucy', age:20},
        ]
      
        app.get('/teachers', (req, res)=>{
        	res.send(`callback(${JSON.stringify(teachers)})`)
        })
      
        app.listen(80, ()=>{
        	console.log('server listen')
        })
      
  • 前端代码
    •   <!DOCTYPE html>
        <html>
            <head>
                <meta charset="UTF-8">
                <title>跨域访问</title>
            </head>
            <body>
                <script type="text/javascript">
        			function  callback(data){
        				console.log('callback 被调用', data)
        			}
        		</script>
        		<script type="text/javascript" src="http://127.0.0.1:80/teachers"></script>
            </body>
        </html>
      
  • 前端代码优化-设置点击按钮再发送请求
    •   <!DOCTYPE html>
        <html>
            <head>
                <meta charset="UTF-8">
                <title>跨域访问</title>
            </head>
            <body>
                <button onclick="getData">获取数据</button>
                <script type="text/javascript">
                    function callback(data){
                        console.log(data)
                    }
                    
                    function getData(){
                        const script = document.createElement('script')
                        script.onload = ()=>{
                            script.remove()
                        }
        
                        script.src = 'http://127.0.0.1:80/teachers'
                        document.body.appendChild(script)
                    }
                
                </script>
            </body>
        </html>
      

7 配置代理解决跨域

  • 这里只介绍通过Nginx反向代理解决跨域。

  • 在nginx配置文件中配置反向代理

    •   server {
               listen       80;
               server_name  localhost;
        
               location / {
                   root   html;
                   index  index.html index.htm;
               }
        		
        		# 配置反向代理
        		location /api {
        			proxy_pass https://www.toutiao.com/hot-event/hot-board/?origin=toutiao_pc;
        			add_header Access-Control-Allow-Origin *;
        		}
        
               error_page   500 502 503 504  /50x.html;
               location = /50x.html {
                   root   html;
               }
        }
      
      
  • 修改Nginx默认index.html页面如下

    •   <!DOCTYPE html>
        <html>
            <head>
                <meta charset="UTF-8">
                <title>跨域访问</title>
            </head>
            <body>
                <button onclick="getNews()">获取新闻</button>
                <script type="text/javascript">
                    async function getNews(){
                        const myurl = "http://192.168.206.146:80/api"
                        let result = await fetch(myurl)
                        let data = await result.json()
                        console.log(data)
                    }
                </script>
            </body>
        </html>
      
  • 在标题4的演示中,直接在浏览器访问 http://192.168.206.146:80,点击获取新闻,会访问对应链接 https://www.toutiao.com,由于存在跨域,因此访问失败。

  • 配置反向单例后,在浏览器访问 http://192.168.206.146:80, 点击获取新闻,会访问 http://192.168.206.146:80/api 这个地址,而 http://192.168.206.146:80/api 实际代理到了 https://www.toutiao.com 地址上。由于服务器之间不存在跨域问题,因此可以正常访问。

  • 效果

    • 在这里插入图片描述
  • 点击获取新闻,可以看到,可以成功获取到数据。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

大草原的小灰灰

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值