Ajax同源策略以及如何解决跨域请求

1. Ajax同源策略

在了解Ajax同源策略之前,我们首先需要知道Ajax请求限制的问题。

什么是Ajax请求限制?

Ajax 只能向自己的服务器发送请求,如果想要向其他服务器发送请求就涉及到跨域请求的问题。

比如现在有一个A网站、有一个B网站,A网站中的 HTML 文件只能向A网站服务器中发送 Ajax 请求,B网站中的 HTML 文件只能向 B 网站中发送 Ajax 请求,但是 A 网站是不能向 B 网站发送 Ajax请求的,同理,B 网站也不能向 A 网站发送 Ajax请求。

在解决这个问题之前,我们就要去理解同源策略的相关概念

1.1 什么是同源策略

Ajax同源策略是由Netscape(网景通信公司)提出的安全策略,它可以让同一网站之间的访问不受限制,但不同网站之间不能随意访问。

Ajax同源策略规定URL地址中的协议(http/https)、域名和端口要都相同,以保证多个页面或者请求来自同一个服务器端,也就是同源,只要有一个不相同,就是不同源。

1.1.1 URL的组成

URL:

URL 通常由几个部分组成,包括协议、服务器地址、端口号、文件路径和查询字符串等,例如 http://www.example.com:8080/index.html?name=Bob

协议:

超文本传输协议(HyperText Transfer Protocol),服务器和浏览器之间传输超文本(HTML、图片等内容)的传送协议。

  • HTTP(Hypertext Transfer Protocol):用于在 Web 浏览器和 Web 服务器之间传输文本、图片、音频、视频等内容。
  • HTTPS(HTTP Secure):是一种基于 SSL/TLS 协议的安全传输协议,可以保证数据传输的安全性。

域名:

在互联网中,域名(Domain Name)是用于便于记忆的网络地址,通常用于访问万维网上的网站。它是由一串用点分隔的名字组成的,例如 www.example.com。其中,.com 称为顶级域名(TLD),example 是一个二级域名,www 则是三级域名。

域名通过一个域名系统(DNS)解析成实际的 IP 地址,以便计算机之间进行通信访问。具体来说,当我们在浏览器中输入一个域名时,浏览器会首先向本地 DNS 服务器发送一个域名解析请求,如果该服务器无法解析,则会向更高层次的 DNS 服务器发送请求,直到找到正确的 IP 地址为止。最终,该 IP 地址用于建立与服务器的连接,以便获取网页内容等资源。

端口号:

端口通常是一个 16 位无符号整数,取值范围为 0 到 65535。

其中,0 到 1023 的端口号被称为“知名端口”(Well-known Port),一般用于标识系统和服务的通信端口,例如 HTTP 使用的默认端口号为 80 。

例如:

  • http://www.example.com/dir/page.html(默认端口号是80)
  • http://www.example.com/dir2/other.html:同源
  • http://example.com/dir/other.html:不同源(域名不同)
  • http://v2.www.example.com/dir/other.html:不同源(域名不同)
  • http://www.example.com:81/dir/other.html:不同源(端口不同)
  • https://www.example.com/dir/page.html:不同源(协议不同)

1.1.2 同源策略的目的
  • 同源政策是为了保证用户信息的安全,防止恶意的网站窃取数据。最初的同源政策是指 A 网站在客户端设置的 Cookie,B网站是不能访问的。
  • 随着互联网的发展,同源政策也越来越严格,在不同源的情况下,其中有一项规定就是无法向非同源地址发送Ajax 请求,如果请求,浏览器就会报错。

1.1.2 演示两个不同源服务器之间发送Ajax请求的限制

在code目录下新建server1目录,然后再server1中新建public目录,在public中新建demo.html页面

<!DOCTYPE html>
<html lang="en">

<head>
    <title>(server1)演示两个不同源服务器之间发送Ajax请求的限制</title>
</head>

<body>
    <button id="btn">点我发送请求</button>

    <!-- 引入封装好的ajax -->
    <script src="../js/ajax.js"></script>
    <script>
        // 获取btn
        var btn = document.getElementById('btn');

        // 为按钮添加单击事件
        btn.onclick = function () {
            // 调用ajax函数
            ajax({
                // 表示访问端口号为3001的不同源的server2服务器
                url: 'http://localhost:3001/test',
                success: function (data) {
                    console.log(data)
                }
            })
        }
    </script>
</body>

</html>

在server1目录下,安装express框架,新建app.js

安装express
npm init -y
npm install express --save
npm list express
// 引入express模块
const express = require('express')

// 引入path模块处理路径
const path = require('path')

// 创建网站服务器对象
const app = express()

// 定义中间件处理静态资源
app.use(express.static(path.join(__dirname, 'public')))

// 监听端口
app.listen(3000)
console.log('服务器已启动,请访问localhost:3000')

在code目录下,新建server2目录,在server2目录下安装express框架,并新建app.js文件

// 引入express模块
const express = require('express')

// 创建网站服务器对象
const app = express()

// 定义路由
app.get('/test', (req, res) => {
    res.send('ok')
})

// 监听端口
app.listen(3001)
console.log('服务器已启动,请访问localhost:3000')

分别切换到server1目录和server2目录,在命令行工具中使用nodemon app.js命令分别启动server1和server2两个不同源的服务器

在这里插入图片描述

在浏览器中访问“http://localhost:3000/demo.html”,点击按钮,查看控制台报错信息

在这里插入图片描述


1.2 JSONP解决跨域请求问题

JSONP(JSON with Padding)是JSON的一种使用模式,它解决了Ajax同源限制的问题。

JSONP请求使用<script>标签来实现。

语法

设置src属性的值为JSONP请求地址,当页面加载时会发起了一个JSONP的跨域请求

<script type=“text/javascript” src=JSONP请求地址">
</script>

JSONP请求地址的组成

  • /jsonp:表示路由的地址

  • ?:表示连接请求参数

  • &:表示连接多个参数

  • jsonpcallback:表示请求参数名称

  • 设置name参数的值为value

http://localhost:3000/jsonp?jsonpcallback=callbackFunction&name=value

示例代码

演示JSONP实现跨域请求

在server1\public目录下新建demo1.html文件,编写HTML代码。

使用<script>向http://localhost:3001/jsonp发起JSONP跨域请求

服务器还使用server1目录下app.js进行启动

<!DOCTYPE html>
<html lang="en">

<head>
    <title>演示JSONP实现跨域请求</title>
</head>

<body>
    <script>
        function fn(data) {
            console.log('客户端的fn函数被调用了')
        }
    </script>
    <!-- 将非同源服务器server2的请求地址写在script标签的src属性中 -->
    <script src="http://localhost:3001/jsonp"></script>
</body>

</html>

在server2目录中,新建jsonp.js文件,编写JavaScript代码

定义“/jsonp”路由

// 引入express模块
const express = require('express')

// 创建网站服务器对象
const app = express()

// 定义路由
app.get('/jsonp', (req, res) => {
    // 发送fn()函数的调用
    res.send('fn()')
})

// 监听端口
app.listen(3001)
console.log('服务器已启动,请访问localhost:3001')

切换到server2目录,使用nodemon jsonp.js启动服务器,同时切换到server1目录下,使用nodemon app.js启动服务器

在这里插入图片描述

在浏览器中打开“http://localhost:3000/demo01.html”,查看运行结果。

fn函数被调用说明,可以进行跨域请求

在这里插入图片描述


1.3 rquest模板解决跨域请求问题

request模块是一个Node.js第三方模块,是对http模块的封装,使用起来更加方便。

使用request模块能够解决服务器端同源限制的问题,它解决跨域请求问题的基本过程

  • 使用Node.js中的request模块在服务器端向其他不同源服务器发送请求。

  • 不同源服务器将结果返回给同源服务器。

  • 在同源服务器中,再把数据返回给浏览器端。

示例代码

下载和安装request模块

npm install request --save

server1\public目录,新建demo2.html文件,编写HTML代码。

<body>
	<button id="btn">点我发送请求</button>
	<script src="/js/ajax.js"></script>
</body>

demo2.html文件中,编写HTML代码。

调用ajax()函数,向http://localhost:3000/server发送GET请求

<script>
    // 获取btn
    var btn = document.getElementById('btn');

    // 给btn绑定事件
    btn.onclick = function () {
        ajax({
            type: 'get',
            url: 'http://localhost:3000/server',
            success: function (data) {
                console.log(data)
            }
        })
    }
</script>

在server1目录中,新建server.js文件,编写JavaScript代码。

  • 当客户端请求 http://localhost:3000/server 时,调用 request 函数向另一个服务器 http://localhost:3001/cross 发送 GET 请求,并将获取到的响应数据返回给客户端。
// 引入 express 模块
const express = require('express');
// 引入 request 模块
const request = require('request');
// 引入 path 模块
const path = require('path');
// 创建 express 实例
const app = express();

// 静态资源访问服务功能
// 设置静态资源根目录为 'public' 文件夹
app.use(express.static(path.join(__dirname, 'public')));

// 定义 /server 路由处理方法
app.get('/server', (req, res) => {
    // 调用 request 模块向 http://localhost:3001/cross 发送 GET 请求
    request('http://localhost:3001/cross', (err, response, body) => {
        // 将响应的数据返回给客户端
        res.send(body);
    });
});

// 监听 3000 端口,启动服务器
app.listen(3000);
console.log('服务器启动成功');

新建server2目录,在该目录下新建cross.js文件,编写JavaScript代码。

定义“/cross”路由

// 引入Express框架
const express = require('express');
// 创建Web服务器
const app = express();
app.get('/cross', (req, res) => {
  res.send('ok');
});
// 监听端口
app.listen(3001);
// 控制台提示输出
console.log('服务器启动成功');

切换到server1和server2目录,在命令行工具中分别使用nodemon server.js和nodemon cross.js分别启动两个不同源的服务器

在这里插入图片描述

在浏览器中打开“http://localhost:3000/demo2.html”,单击“点我发送请求”

在这里插入图片描述


1.1.3 CORS解决跨域请求问题

CORS:全称为 Cross-origin resource sharing,即跨域资源共享,它允许浏览器向跨域服务器发送 Ajax 请求,克服了 Ajax 只能同源使用的限制。

在这里插入图片描述

响应头

//  允许 http://localhost:3000 域名的跨域访问
Access-Control-Allow-Origin: 'http://localhost:3000'
// 如果要允许多个域名,请逐个添加,并在各个域名之间用逗号隔开或使用通配 *
Access-Control-Allow-Origin: '*'

使用CORS解决跨域问题,通常需要在服务端进行配置。

在服务端发送响应时,需要设置正确的CORS响应头,以允许跨域访问。

示例代码

在server1\public目录下,新建demo3.html

<!DOCTYPE html>
<html lang="en">

<head>
    <title>演示cors解决跨域请求问题</title>
</head>

<body>
    <button id="btn">点我发送请求</button>

    <script>
        // 获取btn
        var btn = document.getElementById('btn');

        // 给btn绑定事件
        btn.onclick = function () {
            // 创建XMLHttpRequest对象
            var xhr = new XMLHttpRequest()

            // 初始化请求
            xhr.open('get', 'http://localhost:3001/cors')

            // 设置允许跨域携带cookie
            xhr.withCredentials = true

            // 请求成功回调
            xhr.onload = function () {
                if (xhr.status == 200) {
                    console.log(xhr.responseText)
                }
            }

            // 发送请求
            xhr.send()
        }

    </script>
</body>

</html>

在server2目录下,新建cors.js文件

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

// 允许所有源的跨域访问,并允许携带身份凭证(如 cookie)
app.use((req, res, next) => {
    // 允许http://localhost:3000 跨域访问
    res.setHeader('Access-Control-Allow-Origin', 'http://localhost:3000');
    res.setHeader('Access-Control-Allow-Methods', 'GET, POST, PUT, DELETE');
    res.setHeader('Access-Control-Allow-Headers', 'Content-Type');
    res.setHeader('Access-Control-Allow-Credentials', true);
    next();
});

// GET请求处理
app.get('/cors', (req, res) => {
    res.send('CORS 连接成功!');
});

// 启动Web服务器
app.listen(3001, () => {
    console.log('Web服务器正在监听 3001 端口...');
});
  • 在这个例子中,通过调用Express的 use 方法设置一个中间件,将CORS响应头设置为允许http://localhost:3000的跨域访问,并允许携带身份凭证。next()方法表示继续执行下一个中间件。

  • 在处理GET请求时,通过回调函数(handler)返回一个字符串“CORS 连接成功!”。 注意,如果要对其他类型的请求(如POST、PUT、DELETE)进行CORS处理,需要在响应头中设置 Access-Control-Allow-Methods 头。

  • 总的来说,使用CORS解决跨域问题需要在服务端进行配置,同时需要在客户端进行一些指定头部处理。在服务端进行正确的配置后,就可以允许跨域请求,并与其他域名下的网站进行正常交互了。

切换到server1和server2目录,在命令行工具中分别使用nodemon app.js和nodemon cors.js分别启动两个不同源的服务器

在这里插入图片描述

在浏览器中打开“http://localhost:3000/demo3.html”,单击“点我发送请求”

在这里插入图片描述


1.1.4 常见面试题

如何解决跨域问题?

总结

名称描述
CORS跨域资源共享,需要后端配合设置 Access-Control-Allow-Origin 字段,允许指定的域名跨域请求数据 (了解即可)
JSONP利用浏览器不限制 script 标签跨域的特性,通过 script 标签的 src 属性加载跨域文件
requestrequest模块是一个Node.js第三方模块,是对http模块的封装,使用起来更加方便。使用request模块能够解决服务器端同源限制的问题,它解决跨域请求问题的基本过程。
  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值