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

简介: 互联网发展至今,前端开发者经常面临跨域问题,这是因为浏览器的同源策略限制了不同源的网页之间的数据交互。当尝试从`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

 小结  

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

相关文章
|
3月前
|
缓存 Java 应用服务中间件
2021年9月28日,老是遇到一些非常奇葩的问题。就离谱、好好的一个web项目就莫名奇妙坏了。
开发者在使用IDEA 2020编辑器搭建的SSM框架图书管理系统中,遇到删除功能异常问题,经过一系列尝试后发现是IDEA编译缓存导致的,最终通过重新编译项目解决了问题。
|
6月前
|
JSON 前端开发 JavaScript
详细剖析让前端头疼的跨域问题是怎么产生的,又该如何解决
详细剖析让前端头疼的跨域问题是怎么产生的,又该如何解决
编程要搞明白的东西(一)
编程要搞明白的东西(一)
85 0
|
SQL 安全 前端开发
Web安全性测试包括哪些要点?梳理下,总算搞明白了
Web安全性测试包括哪些要点?梳理下,总算搞明白了
433 0
Web安全性测试包括哪些要点?梳理下,总算搞明白了
|
Java
编程要搞明白的东西(二)
编程要搞明白的东西(二)
88 0
|
机器学习/深度学习 移动开发 前端开发
想加入大厂?看这篇文章也许会帮助到你
相信加入互联网大厂是每个程序员梦寐以求的事情,无论是从工作环境、员工福利,或者说是技术氛围以及接触到的人所给你带来的一些好的机遇,都是值得我们去追求的,因此程序员可以在职业生涯初期、或者在整个职业生涯中加入过大厂,无论对自己的履历还是阅历都是很有帮助的一件事。
105 0
想加入大厂?看这篇文章也许会帮助到你
|
Android开发
ViewBingding?搞!
kotlin-android-extensions插件
205 0
ViewBingding?搞!
|
前端开发 JavaScript
当下做前端开发,不算简单,这篇文章可以让少走很多弯路以及需要掌握的知识
当下做前端开发,不算简单,这篇文章可以让少走很多弯路以及需要掌握的知识
|
存储 缓存 JavaScript
想好怎么学 Servlet规范了嘛?想好了嘛?没想好先看看这篇文章(爆肝之作),先看着然后慢慢想!!
Servlet(Server Applet)是Java Servlet的简称,称为小服务程序或服务连接器,用Java编写的服务器端程序,具有独立于平台和协议的特性,主要功能在于交互式地浏览和生成数据,生成动态Web内容。 狭义的Servlet是指Java语言实现的一个接口,广义的Servlet是指任何实现了这个Servlet接口的类,一般情况下,人们将Servlet理解为后者。Servlet运行于支持Java的应用服务器中。从原理上讲,Servlet可以响应任何类型的请求,但绝大多数情况下Servlet只用来扩展基于HTTP协议的Web服务器。
想好怎么学 Servlet规范了嘛?想好了嘛?没想好先看看这篇文章(爆肝之作),先看着然后慢慢想!!
|
存储 Windows
明明很努力的写博客,为啥没人看?试试这些方法和工具(建议收藏)
明明很努力的写博客,为啥没人看?试试这些方法和工具(建议收藏)
268 0
明明很努力的写博客,为啥没人看?试试这些方法和工具(建议收藏)
下一篇
无影云桌面