写了几年代码,你将跨域问题弄明白了吗?

简介: 互联网发展至今,前端开发者经常面临跨域问题,这是因为浏览器的同源策略限制了不同源的网页之间的数据交互。当尝试从`http://127.0.0.1:14949`访问`http://localhost:3000`的资源时,浏览器会阻止这种请求,因为它缺少“Access-Control-Allow-Origin”响应头,这是CORS(跨域资源共享)机制的要求。

  前言  

互联网辉煌发展二十年后的今天,行业变得越来越卷,搞了多年前端的我们也不得不学习后端开发。刚用nodeJS环境搭建了一个后端接口服务,用ajax访问就出现了下面问题。

大概意思是使用XMLHttpRequest技术从http://127.0.0.1:14949 访问http://localhost:3000资源时被cors策略阻止,请求的资源上没有"访问控制允许源"标头。

 跨域问题  

按照程序员解决问题的思路,快速复制报错信息粘贴百度搜索,很快有了答案,原来是遇到了跨域问题。要理解跨域问题先来理清几个概念。

同源策略

同源策略是浏览器的一个安全功能,不同源的网页脚本在没有明确授权的情况下,不能读写对方资源。所谓同源是指"协议+域名+端口"三者相同。

image.gif

在图A中A服务器有三个资源分别是index.html、test.json、util.js,index.html访问test.json资源数据:  

访问中协议、域名和端口号都相同属于同源访问不会违背浏览器安全限制。

图B中从A服务器index.html访问B服务器test.json资源: image.gif

域名(ip)不属于同源访问,违背浏览器安全限制。

什么是跨域

使用AJAX技术(XMLHttpRequest 对象),从一个源去请求另一个源资源时,违反浏览器同源策略限制,引起的安全问题,称为跨域。 image.gif

备注:localhost和127.0.0.1虽然都指向本机,但也属于跨域

出现以上情况都会引起跨域,前端使用ajax技术访问后端资源时就会出现如下错误:

image.gif

代码演示

  • 后端代码:
// 商品列表
let productList = [
  {
    number: 1001,
    name: 'javascript高级编程',
    url: 'https://img2.baidu.com/it/u=1527170010,3082273564&fm=253&fmt=auto&app=138&f=JPEG?w=500&h=500',
    price: 88.98,
    num: 0,
    state: false,
  },
  {
    number: 1002,
    name: 'css高级编程',
    url: 'https://img2.baidu.com/it/u=1209311252,2459534360&fm=253&fmt=auto&app=138&f=JPEG?w=500&h=500',
    price: 58.58,
    num: 0,
    state: false,
  },
  {
    number: 1003,
    name: 'html高级编程',
    url: 'https://img0.baidu.com/it/u=2950970616,2356748823&fm=253&fmt=auto&app=138&f=JPEG?w=500&h=496',
    price: 48.58,
    num: 0,
    state: false,
  },
]
let http = require('http') // 引入内置http模块
// 创建一个web服务
let server = http.createServer(function (request, response) {
  // response.setHeader('Access-Control-Allow-Origin', '*')// 跨域问题
  response.writeHead(200, { 'content-type': 'text/html;charset=utf-8' })// 解决乱码
  
  productList = JSON.stringify(productList) // 将数组转字符串
  response.write(productList) // 向响应对象写入数据helloworld
  response.end() // 结束写入,发送数据给请求客户端
})
// 启动web服务
server.listen(3000, () => console.log('web服务启动成功,监听3000端口...'))

image.gif

  • 前端代码:
<!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>跨域问题</title>
  </head>
  <body>
    <button class="product-list">获取数据</button>
    <!-- 显示后端数据 -->
    <p></p>
    <script>
      const btn = document.querySelector('.product-list')
      const pEle = document.querySelector('p')
      btn.addEventListener('click', function () {
        getData()
      })
      /**
       *异步获取后端商品列表数据
       */
      function getData() {
        // 1. 创建XMLHttpRequest
        let xhr = new XMLHttpRequest()
        // 2. 建立连接
        xhr.open('get', 'http://localhost:3000')
        // 3. 发送请求
        xhr.send()
        // 4. 接收响应数据
        xhr.onreadystatechange = function () {
          // 4.1 是否响应完成
          if (xhr.readyState === 4) {
            // 4.2 是否成功响应
            if (xhr.status === 200) {
              let data = xhr.responseText // 响应内容
              pEle.innerHTML = data
            } else {
              alert('网络出错 ' + xhr.status)
            }
          }
        }
      }
</script>
  </body>
</html>
  • image.gif  跨域解决方案  

跨域解决方案不少,这里讨论三个常用方法:

1. jsonp技术

2. cors跨域资源共享

3. 代理服务器

JSONP技术

首先,不知道大家有没有注意,不管是我们的script标签的src还是img标签的src,或者说link标签的href他们没有被同源策略所限制,比如我们有可能使用一个网络上的图片,就可以请求得到,<img src="https://ss.bd.com/aa.jpg/>  

jsonp就是使用同源策略这一“漏洞”,实现的跨域请求

其基本原理是利用HTML的<script>标签天生可以跨域这一特点,用其加载另一个域的JSON数据。

加载完成后会自动运行一个回调函数通知调用者。此过程需要另一个域的服务端支持。

该协议的一个要点就是允许用户传递一个callback参数给服务端,然后服务端返回数据时会将这个callback参数作为函数名来包裹住JSON数据,这样客户端就可以随意定制自己的函数来自动处理返回数据了。

  • jsonp示例
    服务端代码:
// 商品列表
let productList = [
  {
    number: 1001,
    name: 'javascript高级编程',
    url: 'https://img2.baidu.com/it/u=1527170010,3082273564&fm=253&fmt=auto&app=138&f=JPEG?w=500&h=500',
    price: 88.98,
    num: 0,
    state: false,
  },
  {
    number: 1002,
    name: 'css高级编程',
    url: 'https://img2.baidu.com/it/u=1209311252,2459534360&fm=253&fmt=auto&app=138&f=JPEG?w=500&h=500',
    price: 58.58,
    num: 0,
    state: false,
  },
  {
    number: 1003,
    name: 'html高级编程',
    url: 'https://img0.baidu.com/it/u=2950970616,2356748823&fm=253&fmt=auto&app=138&f=JPEG?w=500&h=496',
    price: 48.58,
    num: 0,
    state: false,
  },
]
let http = require('http') // 引入内置http模块
// 创建一个web服务
let server = http.createServer(function (request, response) {
  
  response.writeHead(200, { 'content-type': 'text/html;charset=utf-8' })// 解决乱码
  
  productList = JSON.stringify(productList) // 将数组转字符串
  response.write(`callback(${productList})`) // 向响应对象写入数据helloworld
  response.end() // 结束写入,发送数据给请求客户端
})
// 启动web服务
server.listen(3000, () => console.log('web服务启动成功,监听3000端口...'))
  • image.gif 客户端代码:
<!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>jsonp技术实现</title>
    <!-- 
        jsonp技术实现
           1. jsonp技术利用script标签天生可以跨域特点解决跨域问题
             代码动态创建script标签,将请求url地址作为script标签src属性值
           2. jsonp需要服务端支持
               把真正的数据封装到一个函数中一起返回
               {code:1,info:'helloworld'}
               callback({code:1,info:'helloworld'})
               
         缺点: 只支持get请求
     -->
     <!-- <script src="http://localhost:3000/"></script> -->
</head>
<body>
    <script>
        sendProductList('http://localhost:3000')
        function sendProductList(url){
            let scriptEle = document.createElement('script') // <script>
            scriptEle.setAttribute('src',url)
            document.body.appendChild(scriptEle)
        }
        function callback(res){
            let resObj = JSON.parse(res)
            console.log(res)
            console.log(resObj)
        }
</script>
</body>
</html>
  • image.gif jsonp弊端 :  JSONP仅仅用于get请求

跨域资源共享CORS

CORS是一个W3C标准,全称是"跨域资源共享"(Cross-origin resource )。

它允许浏览器向跨源服务器,发出XMLHttpRequest请求,从而克服了AJAX只能同源使用sharing的限制。

这个操作只需服务端修改代码,如果你作为前端现在还没有遇到过跨域问题,很可能是后端使用cors解决了跨域。

// 创建一个web服务
let server = http.createServer(function (request, response) {
  response.setHeader('Access-Control-Allow-Origin', '*')// 跨域问题
  response.writeHead(200, { 'content-type': 'text/html;charset=utf-8' })// 解决乱码
  
  productList = JSON.stringify(productList) // 将数组转字符串
  response.write(productList) // 向响应对象写入数据helloworld
  response.end() // 结束写入,发送数据给请求客户端
})
// 启动web服务
server.listen(3000, () => console.log('web服务启动成功,监听3000端口...'))

image.gif

核心代码为:

response.setHeader('Access-Control-Allow-Origin', '*')// 跨域问题

image.gif

当然在实际开发中设置的代码会更复杂,更完善一些,这里仅作测试,目标理解跨域。

代理服务器

跨域问题引起的核心原因是违反浏览器的安全策略引发安全问题。如果能让访问的资源不直接通过浏览器访问而是通过服务器代发是不是可以解决这个问题呢?

代理服务器技术同样可以帮我们解决跨域问题,这也是现在前端接触最多的跨域解决方案。 image.gif

image.gif

代码演示:

vue3配置 vue.config.js

image.gif

nginx服务器配置

server {
        listen       3000;
        server_name  localhost;
        # proxy the PHP scripts to Apache listening on 127.0.0.1:80
        #
        location /prod-api/ {
            proxy_pass   http://43.136.34.132:8082/; 
        }
    }

image.gif

 小结  

本文介绍了什么是同源策略,跨域问题,跨域错误,解决跨域的三个方案,相信通过本节学习你对跨域会有一个更清晰的认识。

相关文章
|
存储 监控 Java
基于spring boot物流管理系统设计与实现
基于spring boot物流管理系统设计与实现
|
jenkins 持续交付 开发工具
阿里云容器服务Kubernetes之Jenkins X(1)-安装部署实践篇
"Jenkins X is a CI/CD solution for modern cloud applications on Kubernetes." 这是Jenkins社区对于Jenkins X 的官方总结和定义。
7017 0
|
12月前
|
机器学习/深度学习 算法 5G
|
12月前
|
SQL 机器学习/深度学习 数据库
SQL与Python集成:数据库操作无缝衔接
在开始之前,确保你已经安装了必要的Python库,如`sqlite3`(用于SQLite数据库)或`psycopg2`(用于PostgreSQL数据库)。这些库提供了Python与SQL数据库之间的接口。
|
10月前
|
存储 人工智能 程序员
【C语言】一篇通关所有 “关键字”,值得收藏篇!
关键字是编程语言预定义的保留词,代表特定的操作或结构。C语言中的关键字用于定义变量类型、控制语句、存储类、数据类型等。使用这些关键字可以创建函数、控制程序的流程、声明变量和常量等。
1735 0
|
应用服务中间件 Linux 网络安全
2022年超详细在CentOS 7上安装Nginx方法(源码安装)
这篇文章提供了在CentOS 7系统上通过源码安装Nginx的详细步骤,包括从官网下载Nginx源码包、上传至虚拟机、解压、删除压缩包、编译安装前的配置、安装PCRE库(因为Nginx使用PCRE库解析正则表达式)、安装zlib和OpenSSL库(用于支持HTTPS协议)、重新编译Nginx、安装后启动Nginx服务、关闭服务、修改默认端口、以及重启服务测试等步骤。文章还提供了相关命令和操作截图,帮助用户更好地理解和执行安装过程。
2022年超详细在CentOS 7上安装Nginx方法(源码安装)
|
安全 生物认证 数据安全/隐私保护
多因素认证(MFA)
【8月更文挑战第20天】
2525 1
|
SQL 前端开发 Java
springboot项目中使用shiro实现用户登录以及权限的验证
这篇文章详细介绍了如何在Spring Boot项目中集成Apache Shiro框架来实现用户登录和权限验证,包括项目依赖配置、数据库连接、实体类定义、控制器、服务层、Mapper层以及前端页面的实现,并展示了实际效果和过滤器代码。
springboot项目中使用shiro实现用户登录以及权限的验证
|
Java 测试技术 API
SpringBoot单元测试快速写法问题之计算测试用例的分支覆盖率如何解决
SpringBoot单元测试快速写法问题之计算测试用例的分支覆盖率如何解决
|
监控 机器人 API
利用itchat搭建微信机器人详解(附三个实用示例)(中)
2011年1月21日,微信推出第一个正式版本,到现在已有7个年头。从一开始的不被看到好,到现在的用户量超10亿,大众的日常生活越来越离不开微信。人生苦短我用Python,有没有办法通过Python来对我们使用微信提供一些便利呢? 答案肯定是有的,在Github上有一个基于微信网页版接口微信开源库:itchat,通过几十行的代码就能轻松实现一个微信机器人。本章我们就来了解学习这个库,然后通过三个实用案例来帮大家玩转这个库。
1507 0