相信很多人都会被一道面试题问蒙了。Post为什么会发送两次请求?很多人见到这个面试题,都是一脸懵。为什么呢?因为在正常情况下我们看到的POST请求都是发送了一次请求,不应该发送两次请求,如果一个应用系统中出现了POST请求被重复调用的情况,那么一定是系统出现了问题。那么下面我们就深入的了解一下互联网的HTTP请求。来分析一下面试官为什么会问出这样的问题?
什么是同源策略?
同源策略(Same-Origin Policy)是在网络安全领域的一个关键性的安全策略,主要用于Web浏览器中用来保护用户的信息安全。
这个策略规定了在一个Web页面中的脚本文件例如Javascript只能调用并访问来自相同源得资源,而不同去访问来自不同源的资源。这里所说的同源是指,具有相同的协议(例如HTTP或者是HTTPS)、相同的域名和相同的端口号的两个URL。
同源策略主要是为了防止一些恶意的网站脚本对用户在其他网站的敏感信息进行获取,保证了用户的隐私的安全。
当然同源策略在Web安全领域固然重要,但是有时候也限制了某些用户操作,为了解决这些问题,前端开发者通过跨域资源共享、JSONP等方式来实现对于资源的跨域访问,但是这些技术都需要服务端进行对应配置来实现。
如果没有同源策略会出现什么问题?
隐私泄露
有些恶意的网站可以通过某些JavaScript脚本来访问用户在其他网站上的登录信息,例如获取到Cookies信息,获取LocaStorage中的存储信息等,从而导致个人用户信息泄露。
账户劫持
恶意网站可以利用用户在正常网站的上的登录状态信息,来进行跨站请求伪造攻击来劫持用户账户完成一些非法操作。
数据篡改
利用某些JavaScript脚本对网站上的表单内容进行修改,造成虚假信息的提交,影响正常网站数据的完整性。
Cookie劫持
可以利用脚本获取到其他网站的Cookie信息,冒充正常用户来进行一些非法的操作。
DDoS攻击
利用脚本进行分布式拒绝服务(DDoS)攻击,向其他网站发送大量请求,造成服务器过载或崩溃
恶意广告注入
恶意网站可以通过JavaScript注入恶意广告或恶意脚本到其他网站上,对用户进行欺骗或攻击。
如果没有同源策略的控制,Web安全的风险也会增加,用户的个人信息隐私安全将面临巨大的威胁,同时也无法保证系统的安全性和可用性。因此同源策略在Web安全中有着至关重要的作用。
同源策略演示说明?
假设有两个网页:
- 网页A的URL为:http://example.com/pageA.html
- 网页B的URL为:http://example.com/pageB.html
这两个网页具有相同的协议(http)、主机名(example.com),因此它们属于同一个源。
下面,我们来看一些符合同源策略的操作和一些不符合同源策略的操作。
符合同源策略的操作
1、网页A中的JavaScript可以访问网页A的DOM元素
// 在网页A中的JavaScript
var elementA = document.getElementById('elementA');
2、网页A中的JavaScript可以通过XMLHttpRequest对象向同一源下的网页发送Ajax请求
// 在网页A中的JavaScript
var xhr = new XMLHttpRequest();
xhr.open('GET', 'pageB.html', true); // 向同一源下的网页发送请求
xhr.send();
不符合同源策略的操作
1、网页A中的JavaScript无法访问网页B的DOM元素
// 在网页A中的JavaScript
var elementB = document.getElementById('elementB'); // 无法访问网页B的DOM元素
2、网页A中的JavaScript无法通过XMLHttpRequest对象向不同源下的网页发送Ajax请求
// 在网页A中的JavaScript
var xhr = new XMLHttpRequest();
xhr.open('GET', 'http://anotherdomain.com/pageC.html', true); // 试图向不同源下的网页发送请求
xhr.send(); // 由于不符合同源策略,请求将被阻止
上述代码演示了同源策略的基本原理:只有当两个网页具有相同的协议、主机名和端口号时,它们才属于同一源,才能自由地进行交互。否则,根据同源策略,浏览器会限制跨源的操作,以保护用户的安全和隐私。
CORS是什么?
CORS(Cross-Origin Resource Sharing,跨源资源共享),它是一种实现跨域资源共享的机制,用来使得跨域请求变得安全不受限。因为同源策略的限制,JavaScript在发起跨源请求的时候会收到限制,而通过CORS配置就可以允许服务端进行跨源的请求。
通过在HTTP请求头中添加对应的配置信息来告知浏览器哪些源是可以被允许调用的,以及哪些头部信息是可以被使用的。如果在服务器中包含了相应的数据头部信息,那么在浏览器中就允许JavaScript来访问对应的资源数据。
下面是一些常见的头部信息配置
- Access-Control-Allow-Origin:指定允许访问该资源的源,可以是单个源或使用通配符*表示允许所有源。
- Access-Control-Allow-Methods:指定允许的HTTP方法,如GET、POST、PUT、DELETE等。
- Access-Control-Allow-Headers:指定允许的HTTP头部信息,如Content-Type、Authorization等。
- Access-Control-Allow-Credentials:指定是否允许发送身份凭证(如Cookies、HTTP认证信息)进行跨域请求。
通过使用这些CORS头部信息,服务器可以控制允许哪些跨源请求访问其资源,并且可以在需要时进行精细的配置,从而实现更安全的跨域资源共享。
什么是简单请求?什么是预检请求?
在CORS(Cross-Origin Resource Sharing,跨源资源共享)机制中,根据请求类型的不同,可以将跨源请求分为两种类型:简单请求(Simple Request)和预检请求(Preflight Request)。
简单请求(Simple Request)
简单请求是指满足以下所有条件的跨源请求
要求一、使用以下HTTP方法之一
- GET
- HEAD
- POST
要求二、使用以下MIME类型之一
- text/plain
- application/x-www-form-urlencoded
- multipart/form-data
要求三、请求中的自定义头部信息(Custom Headers)数量限制在以下几个常见的头部之一
- Accept
- Accept-Language
- Content-Language
- Content-Type(仅限于上述指定的几种MIME类型)
简单请求不会触发预检请求,浏览器会直接发送跨源请求,并在请求头中添加Origin字段以标识请求来源。服务器可以根据请求头中的Origin字段来判断是否允许跨源访问,并在响应头中添加相应的CORS头部信息,从而实现跨源资源共享。
// 前端JavaScript代码示例
fetch('http://example.com/api/data', {
method: 'GET',
headers: {
'Content-Type': 'application/json', // 自定义头部信息,符合简单请求条件
},
})
.then(response => response.json())
.then(data => console.log(data))
.catch(error => console.error('Error:', error));
预检请求(Preflight Request)
预检请求是指在发送真正的跨源请求之前,浏览器会先发送一个OPTIONS方法的请求到服务器,以确定是否允许发送真正的请求。预检请求包含了一些附加的头部信息,如Origin、Access-Control-Request-Method、Access-Control-Request-Headers等,用于通知服务器即将发送的跨源请求的详细信息。
服务器收到预检请求后,根据预检请求中的信息来决定是否允许真正的跨源请求。如果服务器允许跨源请求,会在预检请求的响应中添加相应的CORS头部信息,并在Access-Control-Allow-Methods和Access-Control-Allow-Headers头部中指定允许的HTTP方法和头部信息。
// 前端JavaScript代码示例
fetch('http://example.com/api/data', {
method: 'POST',
headers: {
'Content-Type': 'application/json', // 自定义头部信息,不符合简单请求条件
},
})
.then(response => response.json())
.then(data => console.log(data))
.catch(error => console.error('Error:', error));
在这个示例中,我们使用了POST方法进行跨源请求,并且设置了Content-Type头部信息为application/json,这不符合简单请求的条件。因此,在发送真正的跨源请求之前,浏览器会先发送一个OPTIONS方法的预检请求到服务器,以确定是否允许发送真正的请求。这也就是在面试题中面试官提到的POST请求发送两次请求的原因。
附带身份凭证的请求与通配请求的区别?
附带身份凭证的请求
附带身份凭证的请求是指某个请求脚本在进行跨源请求的时候携带了身份凭证(如Cookies、HTTP认证信息)。而在CORS中,如果请求中携带了身份凭证,则需要服务器在响应头中明确指定允许请求的源,而不是使用通配符*。如下所示。
fetch('http://example.com/api/data', {
method: 'GET',
credentials: 'include', // 携带身份凭证
})
.then(response => response.json())
.then(data => console.log(data))
.catch(error => console.error('Error:', error));
在这个示例中,fetch函数的配置中指定了credentials选项为'include',表示该请求会携带身份凭证。因此,服务器在响应头中需要明确指定允许请求的源,而不能使用通配符*。
通配请求
通配请求是服务器在响应头中使用通配符*来允许所有源的跨源请求访问资源。通配请求通常用于不需要携带身份凭证的情况下,允许所有源的跨源请求。这种方式也是在开发中很多开发者最常用的方式。直接在SpringBoot的跨域配置中添加如下的配置。
Access-Control-Allow-Origin: *
这样可以保证服务器在响应头中使用了通配符*来表示允许所有源的跨源请求访问资源。这意味着任何来源的请求都可以访问该资源,而不需要明确指定允许的源。
总结
在正常的情况下POST请求其实就是发送了一次请求,而面试官所说的两次请求其实就是在上面提到的预检请求(Preflight Request)中出现的一次OPTIONS请求的检测。这样就形成了POST请求的两次请求,在正常的同源策略下其实是不会发生这种问题的。很多情况下,通过配置的方式都可以正常实现POST请求的调用。
本文暂时没有评论,来添加一个吧(●'◡'●)