跨域请求是否真正发送?
- 答案 :请求能正常发送到服务端并返回结果,但浏览器会拦截响应。
为什么表单可跨域提交,而 AJAX 不行?
- 答案:表单提交后页面跳转,无法读取响应;AJAX 会获取响应内容,浏览器认为不安全,会拦截响应。
为什么要同源限制 重点
同源限制是浏览器的一种安全策略 ,规定协议、域名、端口三者完全相同才属于同源,不同源间的资源访问会受限。其原因主要有以下几点:
保护 数据安全
-
防止 Cookie 等敏感信息泄露 :Cookie 常存储用户登录状态、偏好等敏感信息。若无同源限制,恶意网站能随意读取其他网站的 Cookie。
比如用户登录银行网站后,访问恶意网站时,恶意网站可读取银行网站 Cookie,冒充用户转账等,造成财产损失。同理,LocalStorage 和 IndexDB 中保存的用户数据也会面临泄漏风险 。
-
抵御跨站脚本攻击(XSS) :XSS 攻击中,攻击者往网页注入恶意脚本。同源限制下,恶意脚本不能跨域访问其他网站资源 ,限制了攻击范围和危害。
比如在某论坛页面注入的恶意脚本,无法通过同源限制去获取银行网站用户信息。
-
防范跨站请求伪造攻击(CSRF) :CSRF 攻击时,用户在已登录目标网站情况下,被诱骗访问恶意网站,恶意网站利用用户浏览器向目标网站发恶意请求。同源限制可阻止恶意网站跨域发送携带用户会话 Cookie 的请求,降低 CSRF 攻击风险。
维护网页的 稳定性和一致性
同源策略将不同源网页隔离开,各网页在独立沙箱环境运行 。这样,某个网页代码错误、崩溃,不会影响其他网页 。例如,一个电商网页因代码逻辑错误报错,不会干扰同时打开的新闻网页正常显示和操作,保障浏览器整体稳定运行
。
防止资源滥用和攻击
-
限制跨域 AJAX 请求 :AJAX 可实现网页异步数据交互。若无同源限制,恶意网页能利用浏览器发起大量跨域 AJAX 请求 ,滥用其他网站资源,如占用带宽、增加服务器负载,甚至耗尽资源致网站瘫痪。
-
阻止恶意代码攻击 :同源限制可防止恶意代码跨域获取其他网站敏感资源、利用其他网站服务进行非法操作,保障网站资源安全。 虽然同源限制带来一定开发挑战,但通过 CORS、JSONP、代理服务器等技术可在合规前提下实现跨域操作。
例子
如果浏览器没有同源策略,会存在什么样的安全问题呢。下面从 DOM 同源策略和 XMLHttpRequest 同源策略来举例说明
DOM 同源策略
如果没有 DOM 同源策略,也就是说不同域的 iframe 之间可以相互访问,那么黑客可以这样进行攻击(xss攻击 )。 做一个假网站,里面用 iframe 嵌套一个银行网站mybank.com 。把iframe 宽高啥的调整到页面全部,这样用户进来除了域名,别的部分和银行的网站没有任何差别。 这时如果用户输入账号密码,我们的主网站可以跨域访问到 mybank.com 的 dom 节点,就可以拿到用户的账户密码了。
XMLHttpRequest 同源策略
如果没有XMLHttpRequest 同源策略,那么黑客可以进行 CSRF(跨站请求伪造) 攻击。用户登录了自己的银行页面 mybank.com,mybank.com 向用户的 cookie 中添加用户标识。用户浏览了恶意页面 evil.com,执行了页面中的恶意 AJAX 请求代码。evil.com 向 mybank.com 发起 AJAX HTTP 请求,请求会默认把 mybank.com 对应 cookie 也同时发送过去。银行页面从发送的 cookie 中提取用户标识,验证用户无误,response 中返回请求数据。此时数据就泄露了。而且由于 Ajax 在后台执行,用户无法感知这一过程。因此,有了浏览器同源策略,我们才能更安全的上网。
介绍同源策略 重点
同源策略(Same-Origin Policy)详解
1. 什么是同源策略?
同源策略 是Netscape
提出的一个安全策略
,它是浏览器最核心也是最基本 的安全机制,用于限制不同源的网页脚本(如 JavaScript)之间的交互 。目的是防止恶意网站通过脚本窃取用户数据或进行未授权的操作 。浏览器在执行脚本的时候,都会检查这个脚本属于哪个页面,即检查是否同源,只有同源的脚本才会被执行;而非同源的脚本在请求数据的时候,浏览器会报一个异常,提示拒绝访问。
2. 如何定义"同源"?
两个 URL 的以下三部分完全一致时,视为同源:
- 协议 (Protocol):如
http
vshttps
- 域名 (Host):如
example.com
vssub.example.com
- 端口 (Port):如
:80
(默认) vs:8080
示例1:
URL1 | URL2 | 是否同源 | 原因 |
---|---|---|---|
http://a.com/index.html |
http://a.com/api |
是 | 协议、域名、端口一致 |
https://a.com |
http://a.com |
否 | 协议不同(HTTP vs HTTPS) |
http://a.com:80 |
http://a.com:8080 |
否 | 端口不同 |
http://a.com |
http://b.com |
否 | 域名不同 |
示例2:
http://localhost:8080/index.html
和 http://127.0.0.1:8080/index.html
不同源 。同源策略规定,只有协议、域名、端口三者都相同才属于同源 。虽然 localhost
在大多数系统中通过 hosts
文件映射到 127.0.0.1
,但从浏览器同源策略的判断标准来看:
http://localhost:8080/index.html
中,域名是localhost
。http://127.0.0.1:8080/index.html
中,域名是127.0.0.1
。
所以:二者域名不同,不满足同源策略中 "同源" 的要求,所以它们不是同源的。
3. 同源策略的限制范围
同源策略限制的情况:
1、Cookie、LocalStorage 和 IndexDB 无法读取
2、DOM 和 JS对象无法获得
3、AJAX 请求不能发送
注意:对于像 img、iframe、script 等标签的 src 属性是特例,它们是可以访问非同源网站的资源的
同源策略主要限制以下操作:
3.1 DOM 访问限制
-
跨域 iframe 访问 :
父页面无法通过 JavaScript 访问不同源子页面的 DOM。javascript// 父页面尝试访问子页面 DOM(不同源时会报错) const iframe = document.getElementById('my-iframe'); const childDoc = iframe.contentDocument; // 报错:Blocked by Same-Origin Policy
3.2 网络请求限制
-
AJAX/Fetch 跨域请求 :
默认禁止通过XMLHttpRequest
或Fetch API
发送跨域请求。javascriptfetch('https://api.other.com/data') // 不同源 .then(response => response.json()) .catch(error => console.error('被浏览器拦截'));
3.3 数据存储隔离
-
Cookie、LocalStorage、SessionStorage、IndexDB :
不同源的页面无法互相访问本地存储数据。javascript// 在 https://a.com 的页面中 document.cookie = 'name=John'; // 在 https://b.com 的页面中无法读取该 Cookie
4. 同源策略的例外情况
虽然同源策略限制严格,但以下场景允许跨域:
4.1 跨域资源加载
-
部分标签允许跨域加载资源 :
<img>
、<video>
、<iframe>
、<script>
、<link>
、 等标签可加载跨域资源,但 JavaScript 无法直接读取内容。html<!-- 允许加载跨域图片 --> <img src="https://other.com/image.jpg"> <!-- 允许加载跨域脚本(JSONP 原理) --> <script src="https://api.other.com/data?callback=handleData"></script>
4.2 CORS(跨源资源共享)
-
服务端显式授权 :
若服务器返回Access-Control-Allow-Origin
头,浏览器允许跨域请求。httpAccess-Control-Allow-Origin: https://your-site.com # 允许指定源 Access-Control-Allow-Origin: * # 允许所有源(慎用)
4.3 其他豁免场景
- 表单提交 :
HTML 表单(<form>
)允许提交到不同源,但无法读取响应结果。 - WebSocket :
WebSocket 不受同源策略限制,但需服务端支持。
5. 同源策略的实际影响与解决方案
5.1 常见问题场景
- 前端调用第三方 API :
若未配置 CORS,浏览器会拦截响应。 - 微前端架构 :
子应用间跨域通信需通过postMessage
或代理解决。
5.2 主流解决方案
方案 | 原理 | 适用场景 |
---|---|---|
CORS | 服务端设置响应头声明允许的源 | 生产环境主流方案 |
JSONP | 利用 <script> 标签跨域加载脚本,通过回调函数接收数据 |
仅支持 GET,旧项目兼容 |
代理服务器 | 前端请求同源代理,由代理转发至目标服务器 | 无法修改服务端配置时 |
postMessage | 跨窗口通信(如 iframe、新标签页)传递数据 | 跨域页面间可控通信 |
WebSocket | 使用 WebSocket 协议绕过同源策略 | 实时双向通信场景 |
6. 绕过同源策略的风险
尽管存在多种跨域方案,但需注意:
- CORS 配置不当 :
滥用Access-Control-Allow-Origin: *
可能导致 CSRF 攻击。 - JSONP 安全性 :
若第三方脚本不可信,可能注入恶意代码。 - 代理服务器性能 :
频繁请求可能增加服务器负载。
7. 总结
同源策略是浏览器安全的基石,通过限制跨域交互保护用户隐私。开发者需根据场景选择合法、安全的跨域方案(如 CORS 或代理),避免因配置不当引发安全漏洞。理解其原理与限制,是构建健壮 Web 应用的关键。
为什么会存在跨域及常见跨域的解决办法 / 如何解决跨域的问题 (阿里/滴滴/字节/字节/喜马拉雅) 重点
什么是跨域?
跨域是指在 Web 开发中,浏览器出于安全考虑,限制一个网页(源)对另一个网页(源)的资源进行访问的情况
为什么会跨域?
跨域问题的存在主要是因为浏览器的同源策略限制。同源策略是浏览器的一种安全机制,
常见的解决跨域方案 主流5种
-
JSONP :利用
<script>
标签的src
属性不受同源策略限制的特性 ,通过动态插入标签来请求不同源的数据。JSONP只支持GET请求,并且需要在服务器端进行相应的配合 。优点 :简单,兼容好,缺点:get 、不安全、数量传输数量小、数据格式单一。 -
CORS(跨域资源共享) :(主流方案:标准化、安全、支持所有 HTTP 方法 )它是现代 Web 开发的标准跨域方案。CORS 通过服务端设置 HTTP 响应头 ,告知浏览器允许特定页面的跨域请求 。CORS 支持各种HTTP请求方法,并且更加灵活和安全。如果服务器端支持 CORS,可以通过设置 Access-Control-Allow-Origin 来实现跨域。如果浏览器检测到相应的设置,就会允许 Ajax 进行跨域访问。
-
代理服务器 :通过搭建一个代理服务器来转发请求 ,使得前端可以通过代理服务器来间接访问不同源的资源。这样,前端请求实际上是发往同源的代理服务器,再由代理服务器去请求不同源的资源。
-
使用window.postMessage :window.postMessage是HTML5引入的一个新的API ,允许来自不同源的脚本进行通信。通过监听window对象的message事件,可以接收其他窗口发送过来的消息。
-
设置document.domain :如果两个页面属于同一个基础域名下的不同子域名,可以通过设置
document.domain
为相同的基础域名来实现跨域。但是,这种方式存在限制,并且可能引入其他安全风险。 -
修改 window.name 实现跨域 :window 对象有一个 name 属性,在一个窗口(window)的生命周期内,窗口中载入的所有页面都是共享一个 window.name 的,每个页面对 window.name 都有读写的权限。载入过的所有页面的 window 对象,将持久地存储 name 属性。
-
location.hash :不同源的页面之间虽然受同源策略限制无法直接通信,但可以通过修改
location.hash
来传递数据。因为hash
的改变不会导致页面的刷新,且可以在不同源的页面之间共享。 通常会借助<iframe>
来实现跨域通信,一个页面作为父页面,另一个页面作为子页面(通过<iframe>
嵌入),通过修改hash
值并监听hashchange
事件来实现数据的传递和接收。 -
websocket跨域 :WebSocket 是一种 全双工通信协议 ,其设计天然支持跨域通信 。浏览器在建立 WebSocket 连接时,会通过 HTTP 握手请求升级协议,且不受同源策略限制 。始终验证
Origin
头并使用wss://
加密连接 -
通过 Flash : 可以实现跨域。尽管 Flash 技术已被现代浏览器淘汰 ,但在历史项目中可能会遇到使用 Flash 实现跨域的场景。其核心依赖
crossdomain.xml
策略文件 来声明允许的跨域访问权限 -
NodeJS中间件代理跨域
1、jsonp(json+padding)
2、document.postMessage + iframe跨域
3、document.domain + iframe跨域
4、window.name + iframe跨域
5、location.hash+ iframe跨域
跨域方法对比 重点
以下是 Web 前端跨域方法的全面总结,涵盖主流方案及传统技巧,按实现原理分类整理:
一、服务端配置类
方法 | 原理 | 适用场景 | 特点 |
---|---|---|---|
CORS | 服务端设置 Access-Control-Allow-Origin 等响应头声明允许的源 |
主流方案,生产环境首选 | 标准化、安全、支持所有 HTTP 方法 |
代理服务器 | 前端请求同源代理,代理转发到目标服务器 | 无法修改服务端配置时 | 隐藏真实接口、兼容性好 |
Nginx 反向代理 | 通过反向代理配置转发请求 | 生产环境部署优化 | 高性能、需运维知识 |
方案 | 适用场景 | 优点 | 缺点 |
---|---|---|---|
CORS | 主流生产环境 | 标准化、支持所有方法 | 需服务端配合 |
JSONP | 旧项目兼容 | 兼容性好 | 仅 GET、安全性低 |
反向代理 | 隐藏接口、生产环境 | 无需修改客户端代码 | 需运维知识 |
WebSocket | 实时通信(如聊天) | 低延迟、全双工 | 协议复杂度高 |
postMessage | 跨窗口通信 | 安全灵活 | 需双方协同 |
二、HTML/JS 特性类
方法 | 原理 | 适用场景 | 特点 |
---|---|---|---|
JSONP | 利用 <script> 标签的跨域加载能力,通过回调函数接收数据 |
旧项目兼容、简单 GET 请求 | 兼容性好、安全性低 |
WebSocket | WebSocket 协议天然支持跨域 | 实时双向通信(如聊天室) | 全双工、低延迟 |
postMessage | 跨窗口/iframe 间通过 window.postMessage 通信 |
跨域页面可控通信 | 安全灵活、需双方协同 |
document.domain | 主域相同的子域间设置相同 document.domain 实现同源 |
同一主域下的子域通信 | 仅限特定场景、逐渐淘汰 |
三、传统 Hack 方案
方法 | 原理 | 适用场景 | 特点 |
---|---|---|---|
window.name | 利用 window.name 跨页面保留数据的特性,通过 iframe 跳转传递数据 |
历史遗留项目 | 兼容旧浏览器、实现复杂 |
location.hash | 父页面与 iframe 通过 URL 的 hash 值传递数据 | 简单单向通信 | 数据量小、安全性低 |
Flash 跨域 | 依赖 crossdomain.xml 策略文件定义允许的域 |
已淘汰技术 | 过时方案、存在安全漏洞 |
四、浏览器/工具类
方法 | 原理 | 适用场景 | 特点 |
---|---|---|---|
禁用浏览器同源策略 | 启动浏览器时添加 --disable-web-security 参数 |
本地测试环境 | 存在安全隐患,严禁生产使用 |
浏览器插件(CORS Unblock) | 插件自动添加 CORS 头或代理请求 | 临时调试 | 非生产方案、依赖插件 |
抓包工具代理(Charles) | 通过本地代理工具转发请求 | 开发调试 | 需手动配置规则 |
五、其他补充方案
方法 | 原理 | 适用场景 | 特点 |
---|---|---|---|
图像 Ping(Image Beacon) | 使用 <img> 标签发送 GET 请求(仅支持单向通信) |
数据上报、埋点 | 无响应数据、简单易用 |
Service Worker 拦截 | 通过 Service Worker 拦截请求并修改 | 高级场景(离线缓存、请求改写) | 需 HTTPS 支持、实现复杂 |
六、方案对比与选择建议
场景 | 推荐方案 | 原因 |
---|---|---|
现代 Web 应用 | CORS 或代理服务器 | 标准化、安全可控、支持复杂请求 |
旧项目维护 | JSONP 或代理服务器 | 兼容老旧浏览器、无需服务端改动 |
跨窗口/iframe 通信 | postMessage | 安全灵活、原生支持 |
实时双向通信(如聊天) | WebSocket | 低延迟、全双工 |
本地开发调试 | 开发工具代理(Webpack/Vite)或浏览器插件 | 快速验证、无需服务端配合 |
七、安全注意事项
- CORS :避免使用
Access-Control-Allow-Origin: *
,应动态校验可信源。 - JSONP:确保第三方脚本可信,防止 XSS 攻击。
- 代理服务器 :限制代理范围,防止被滥用转发任意请求。
- postMessage :严格验证
event.origin
,防止恶意消息注入。
通过合理选择跨域方案,可兼顾功能、安全与兼容性。CORS 是现代化应用的黄金标准,其他方案可作为特定场景的补充。
怎么用JSONP实现跨域
JSONP的含义
JSONP(JSON with Padding)是一种绕过浏览器同源策略的跨域数据请求方法。在 JSONP 中,"padding" 有 "填充""包裹" 的意思。 JSONP 是将 JSON 数据包裹(填充)在一个合法的 JavaScript 函数调用中,以实现跨域获取数据例如,服务器返回 callback({ "name": "John", "age": 30 })
,这里的 { "name": "John", "age": 30 }
是原本要返回的 JSON 数据,被 "填充" 到了 callback
函数中,客户端收到后会执行这个函数,从而获取到数据。所以 "padding" 形象地描述了将 JSON 数据包装在函数中的这个过程。
JSONP 实现跨域详解
1. JSONP 原理
JSONP(JSON with Padding)是一种绕过浏览器同源策略的跨域数据请求方法,利用 <script>
标签的 src
属性不受同源策略限制的特性。其核心步骤如下:
- 客户端:定义回调函数 :提前声明一个全局函数,用于接收数据。
- 客户端:动态创建
<script>
标签 :通过 JavaScript 动态生成<script>
标签,并将目标 URL 的callback
参数指向回调函数名。 - 服务器:返回调用代码 :服务器将数据包裹在回调函数调用中 (如
callbackFunction(data)
)。 - 客户端:执行回调 :浏览器加载脚本后自动执行回调函数,完成数据接收。
2. JSONP 实现步骤
2.1 客户端代码
html
<!-- 定义回调函数 -->
<script>
function handleResponse(data) {
console.log('Received:', data);
}
</script>
<!-- 动态加载跨域脚本 -->
<script>
const script = document.createElement('script');
script.src = 'https://api.example.com/data?callback=handleResponse';
document.body.appendChild(script);
</script>
2.2 服务端代码(Node.js 示例)
javascript
const http = require('http');
const url = require('url');
const server = http.createServer((req, res) => {
const parsedUrl = url.parse(req.url, true);
const callbackName = parsedUrl.query.callback;
const data = { name: 'John', age: 30 };
// 返回调用客户端回调的 JS 代码
res.writeHead(200, { 'Content-Type': 'application/javascript' });
res.end(`${callbackName}(${JSON.stringify(data)})`);
});
server.listen(3000);
3. JSONP 关键细节
3.1 回调函数名动态生成
避免全局命名冲突,使用唯一标识符(如时间戳)动态生成回调函数名:
javascript
function jsonp(url) {
const callbackName = `jsonp_${Date.now()}`;
return new Promise((resolve, reject) => {
window[callbackName] = (data) => {
delete window[callbackName]; // 清理全局函数
document.body.removeChild(script);
resolve(data);
};
const script = document.createElement('script');
script.src = `${url}?callback=${callbackName}`;
script.onerror = () => reject(new Error('Request failed'));
document.body.appendChild(script);
});
}
// 使用示例
jsonp('https://api.example.com/data')
.then(data => console.log(data))
.catch(err => console.error(err));
3.2 超时处理
防止服务器未响应导致脚本挂起:
javascript
function jsonp(url, timeout = 5000) {
return new Promise((resolve, reject) => {
const timer = setTimeout(() => {
reject(new Error('Timeout'));
cleanup();
}, timeout);
function cleanup() {
clearTimeout(timer);
delete window[callbackName];
document.body.removeChild(script);
}
// ...(同上动态生成回调函数和 script 标签)...
});
}
3.3 安全性问题
- XSS 风险 :JSONP 依赖服务器返回任意 JS 代码,需确保服务器可信。
- CSRF 防御:建议添加 Token 验证,但 JSONP 本身不支持自定义请求头。
4. JSONP 的局限性
优点 :简单,兼容好,缺点:get 、不安全、数量传输数量小,数据格式单一
限制 | 说明 |
---|---|
仅支持 GET 请求 | 无法发送 POST、PUT 等复杂请求 |
无错误处理机制 | 依赖 onerror 事件,但部分浏览器不支持 |
数据格式固定 | 必须返回 JS 代码(如 callback(data) ),无法直接使用 JSON 或其他格式 |
全局污染风险 | 动态创建全局函数可能与其他脚本冲突 |
5. JSONP 与 CORS 对比
特性 | JSONP | CORS |
---|---|---|
请求类型 | 仅 GET | 支持所有 HTTP 方法 |
安全性 | 低(依赖信任服务器) | 高(通过 HTTP 头精细控制) |
兼容性 | 支持所有浏览器(包括老旧浏览器) | 需 IE10+ 或现代浏览器 |
复杂度 | 简单,无需服务端特殊配置(需支持) | 需服务端配置响应头 |
6. 适用场景
- 旧项目兼容 :需在不支持 CORS 的旧版浏览器中跨域。
- 简单数据获取 :仅需 GET 请求且数据量较小的场景。
- 第三方 API 支持:某些传统 API 仅提供 JSONP 接口(如部分天气 API)。
7. 总结
JSONP 通过 <script>
标签巧妙绕过同源策略 ,是跨域问题的经典解决方案。尽管在现代开发中逐渐被 CORS 取代,使用场景逐渐减少,但理解其原理仍对处理兼容性问题和学习浏览器安全机制有重要意义。使用时 需注意安全性和功能限制,优先选择 CORS 等更安全的方案。
JSONP方案需要服务端怎么配合(沪江)
1.接收回调函数参数
服务端需要能够识别并接收客户端在请求中传递的回调函数名参数。通常,客户端会将回调函数名作为 URL 的一个参数传递给服务端 ,例如 http://example.com/api/data?callback=jsonpCallback
,服务端要从请求的参数中提取出 callback
参数的值 ,即 jsonpCallback
。
2.包装数据
服务端将数据包装在一个函数调用中 ,函数名就是从客户端请求中获取的回调函数名。例如,服务端原本要返回的数据是 { "name": "John", "age": 30 }
,那么在 JSONP 中,它会将数据包装成 jsonpCallback({ "name": "John", "age": 30 })
的形式。
3.设置响应头
服务端需要设置正确的响应头,以确保客户端能够正确地解析和处理响应 。一般来说,需要设置 Content - Type
头为 application/javascript
,表示返回的是一段 JavaScript 代码。
以下是一个简单的示例,展示了在 Node.js 中使用 Express 框架实现 JSONP 接口的基本代码:
ini
const express = require('express');
const app = express();
app.get('/api/data', (req, res) => {
// 获取回调函数名
const callback = req.query.callback;
// 假设这是从数据库或其他地方获取的数据
const data = { "name": "John", "age": 30 };
// 包装数据为JSONP格式
const jsonpData = `${callback}(${JSON.stringify(data)})`;
// 设置响应头
res.set('Content - Type', 'application/javascript');
// 返回JSONP数据
res.send(jsonpData);
});
const port = 3000;
app.listen(port, () => {
console.log(`Server running on port ${port}`);
});
这段代码创建了一个简单的 Express 服务器,监听在 3000 端口。当客户端访问 /api/data
接口时,服务器会获取客户端传递的回调函数名,将数据包装成 JSONP 格式,并设置正确的响应头后返回给客户端。
JSONP为什么不支持post方法(兑吧)
JSONP(JSON with Padding)不支持 POST 方法,主要和其实现原理以及<script>
标签的特性相关,以下是具体解释:
依赖<script>
标签实现跨域
<script>
标签本身在发起请求时,只能使用 GET 方法 ,这是 HTML 规范所规定的。当你在 HTML 中使用<script>
标签引入外部脚本时,例如<script src="http://example.com/script.js"></script>
,浏览器会自动以 GET 请求的方式去获取该脚本文件。所以,基于<script>
标签实现的 JSONP 也就只能使用 GET 方法。
缺乏请求体设置能力
POST 方法通常用于向服务器提交数据,它会在请求中包含一个请求体,用于携带要提交的数据。而<script>
标签在发起请求时,没有提供设置请求体的机制 。它只是简单地通过src
属性指定要加载的资源的 URL,无法像 XMLHttpRequest 或 Fetch API 那样设置请求方法为 POST 并携带请求体数据。
综上所述,由于 JSONP 依赖<script>
标签实现跨域,而<script>
标签本身的限制使得 JSONP 只能支持 GET 方法,无法支持 POST 方法。如果需要使用 POST 方法进行跨域请求,可以考虑使用其他跨域解决方案,如 CORS(跨域资源共享)。
JSON和JSONP的区别
1、JSON:是一种轻量级的数据交换格式。
2、JSONP:是JSON的一种"使用模式",可用于解决主流浏览器的跨域数据访问的问题。一种非官方跨域数据交互协议 。
3、json的核心是通过XmlHttpRequest获取非本页内容,而jsonp的核心则是动态添加
JSON和Object的区别
JSON(JavaScript Object Notation)和 Object(对象)在概念、语法、使用场景等方面存在明显区别,下面详细介绍:
概念方面
- JSON :是一种轻量级的数据交换格式 ,用于在不同系统之间传输和存储数据。它独立于编程语言,很多语言都有处理 JSON 数据的库,这让不同语言的应用程序之间能方便地交换数据。
- Object :在编程里是一种数据结构 ,用来组织和存储相关数据与功能。不同编程语言中对象的实现和特性不同,但一般都有属性和方法。
语法规则方面
-
JSON:
- 数据以键值对形式存在,键必须是字符串,且要用双引号括起来。
- 值可以是字符串、数字、布尔值、
null
、数组或另一个 JSON 对象。 - 例如:
json{ "name": "John", "age": 30, "isStudent": false, "hobbies": ["reading", "swimming"], "address": { "city": "New York", "street": "123 Main St" } }
-
Object:
- 语法因编程语言而异。以 JavaScript 为例,键可以是字符串、数字或符号,引号可省略。
- 值可以是任意数据类型,包括函数。
- 例如:
javascriptconst person = { name: 'John', age: 30, isStudent: false, hobbies: ['reading', 'swimming'], address: { city: 'New York', street: '123 Main St' }, sayHello: function() { console.log('Hello!'); } };
用途方面
- JSON :主要用于数据传输和存储。比如在 Web 应用中,客户端和服务器之间交换数据时,服务器可以将数据以 JSON 格式返回给客户端,客户端解析后使用。
- Object :用于在程序内部表示和操作数据 。开发者可以创建对象来封装数据和行为,实现面向对象编程。
解析和序列化方面
- JSON :需要进行解析和序列化操作。在 JavaScript 里,可使用
JSON.parse()
方法把 JSON 字符串解析成 JavaScript 对象,用JSON.stringify()
方法把 JavaScript 对象序列化为 JSON 字符串。 - Object:在程序内部可直接使用和操作,无需额外的解析和序列化步骤。
详细讲一讲CORS
CORS(跨域资源共享)详解
1. CORS 是什么?
CORS(Cross-Origin Resource Sharing)是一种基于 HTTP 头部的安全机制,允许浏览器向不同源的服务器发起跨域请求 。它是 W3C 标准,服务端显式声明允许的源、方法、头信息 ,浏览器根据这些声明决定是否放行跨域请求。
2. CORS 核心流程
2.1 简单请求(Simple Request)
满足以下条件时,浏览器直接发送请求:
- 方法限制:GET、POST、HEAD
- 头限制 :仅包含以下头:
Accept
、Accept-Language
、Content-Language
、Content-Type
(值限于text/plain
、multipart/form-data
、application/x-www-form-urlencoded
)
流程:
- 浏览器发送请求,自动附加
Origin
头(如Origin: https://your-site.com
)。 - 服务端返回响应,包含
Access-Control-Allow-Origin
头。 - 浏览器验证
Access-Control-Allow-Origin
是否匹配当前源。
2.2 预检请求(Preflight Request)
当请求不满足简单请求条件时 (如使用 PUT
、DELETE
,或包含自定义头),浏览器会先发送 OPTIONS
预检请求,询问服务器是否允许实际请求。
流程:
-
浏览器发送
OPTIONS
请求,包含以下头:Origin
:当前源Access-Control-Request-Method
:实际请求方法 (如PUT
)Access-Control-Request-Headers
:实际请求的自定义头 (如X-Custom-Header
)
-
服务端响应预检请求,返回允许的策略:
httpAccess-Control-Allow-Origin: https://your-site.com Access-Control-Allow-Methods: GET, POST, PUT Access-Control-Allow-Headers: X-Custom-Header Access-Control-Max-Age: 86400 // 预检结果缓存时间(秒)
-
浏览器验证通过后,发送实际请求。
3. 服务端配置详解
3.1 必需响应头
响应头 | 作用 |
---|---|
Access-Control-Allow-Origin |
允许的源(如 https://your-site.com 或 * ) |
Access-Control-Allow-Methods |
允许的 HTTP 方法(如 GET, POST, PUT ) |
Access-Control-Allow-Headers |
允许的请求头(如 Content-Type, Authorization ) |
3.2 可选响应头
响应头 | 作用 |
---|---|
Access-Control-Allow-Credentials |
是否允许携带 Cookie (值为 true ,需与 Access-Control-Allow-Origin 非 * 配合) |
Access-Control-Max-Age |
预检请求缓存时间(减少频繁预检) |
Access-Control-Expose-Headers |
允许前端访问的响应头(默认仅暴露简单头:Cache-Control 、Content-Language 等) |
4. 服务端代码示例
Node.js (Express)
javascript
const express = require('express');
const app = express();
// CORS 中间件
app.use((req, res, next) => {
const allowedOrigins = ['https://your-site.com', 'http://localhost:3000'];
const origin = req.headers.origin;
// 动态设置允许的源
if (allowedOrigins.includes(origin)) {
res.setHeader('Access-Control-Allow-Origin', origin);
}
res.setHeader('Access-Control-Allow-Methods', 'GET, POST, PUT, DELETE, OPTIONS');
res.setHeader('Access-Control-Allow-Headers', 'Content-Type, Authorization');
res.setHeader('Access-Control-Allow-Credentials', 'true'); // 允许 Cookie
res.setHeader('Access-Control-Max-Age', '86400');
// 处理 OPTIONS 预检请求
if (req.method === 'OPTIONS') {
return res.sendStatus(200);
}
next();
});
// 示例路由
app.get('/data', (req, res) => {
res.json({ message: 'CORS enabled!' });
});
app.listen(3000);
5. 前端请求配置
携带 Cookie
javascript
fetch('https://api.example.com/data', {
method: 'GET',
credentials: 'include' // 必须设置
});
自定义头
javascript
fetch('https://api.example.com/data', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'X-Custom-Header': 'value'
},
body: JSON.stringify({ key: 'value' })
});
6. 常见问题与解决方案
问题 1:预检请求失败
- 表现 :浏览器控制台报错
OPTIONS 404
或CORS preflight failed
。 - 原因 :服务端未正确处理
OPTIONS
方法或未返回正确的 CORS 头。 - 解决 :确保服务端对所有
OPTIONS
请求返回 200 状态码并包含 CORS 头。
问题 2:Cookie 未携带
- 表现 :请求未携带 Cookie,或服务端返回
403 Forbidden
。 - 原因 :前端未设置
credentials: 'include'
,或服务端Access-Control-Allow-Origin
使用了*
。 - 解决 :前端设置
credentials
,服务端指定具体域名而非*
。
问题 3:响应头未暴露
- 表现 :前端无法读取自定义响应头(如
X-Token
)。 - 原因 :服务端未设置
Access-Control-Expose-Headers
。 - 解决 :服务端添加
Access-Control-Expose-Headers: X-Token
。
7. 安全最佳实践
- 严格限制允许的源 :
- 避免使用
Access-Control-Allow-Origin: *
,动态验证请求的Origin
头。
- 避免使用
- 最小化允许的方法和头 :
- 仅开放必要的 HTTP 方法和请求头。
- 保护敏感操作 :
- 对
PUT
、DELETE
等写操作强制使用预检请求。
- 对
- 监控与日志 :
- 记录异常的跨域请求,防范 CSRF 攻击。
8. CORS 与其他跨域方案对比
特性 | CORS | JSONP | 代理服务器 |
---|---|---|---|
请求类型 | 支持所有 HTTP 方法 | 仅 GET | 所有方法 |
安全性 | 高(细粒度控制) | 低(依赖脚本注入) | 依赖代理服务器安全性 |
兼容性 | 现代浏览器(IE10+ | 全浏览器 | 全浏览器 |
实现复杂度 | 需服务端配置 | 前后端简单约定 | 需搭建代理服务 |
9. 总结
CORS 是现代 Web 应用跨域的首选方案 ,通过服务端声明策略实现细粒度控制。开发者需注意:
- 预检请求 :正确处理
OPTIONS
方法和返回头。 - 凭证安全:避免在开放域下携带 Cookie。
- 动态配置 :根据请求源动态设置
Access-Control-Allow-Origin
。
通过合理配置 CORS,可兼顾功能与安全,为前后端分离架构提供可靠支持。
CORS实现跨域的过程 / 加上CORS之后从发起到请求正式成功的过程(沪江)
当加上 CORS 之后,从发起到请求正式成功一般会经历以下过程:
简单请求(以 GET、POST 等简单方法且无特殊请求头的请求为例)
- 发起请求 :客户端(通常是浏览器)向跨域的服务器发送请求,例如使用
fetch
或XMLHttpRequest
等 API。请求头中会包含Origin
字段,它表示请求发起的源,包括协议、域名和端口号。 - 服务器接收请求 :服务器接收到请求后,会检查
Origin
字段。然后根据自身的 CORS 配置,判断该源是否被允许访问。 - 服务器响应 :如果
Origin
是允许的,服务器会在响应头中添加Access - Control - Allow - Origin
字段,其值为允许的源(可以是具体的源,也可能是*
表示允许所有源)。同时,可能还会添加其他相关的 CORS 响应头,如Access - Control - Allow - Credentials
(如果涉及到跨域携带凭证)等。然后将响应数据发送给客户端。 - 客户端处理响应 :浏览器接收到响应后,检查
Access - Control - Allow - Origin
等 CORS 相关头信息。如果这些头信息表明请求是被允许的,浏览器就会正常处理响应数据,将其传递给前端应用程序进行进一步处理和展示。
复杂请求(如 PUT、DELETE 方法或带有自定义请求头的请求)
-
发起预检请求(OPTIONS) :客户端首先会发送一个预检请求,使用
OPTIONS
方法。请求头中包含Origin
字段,同时还会有Access - Control - Request - Method
字段,用于告知服务器实际请求将使用的方法(如PUT
、DELETE
等),以及Access - Control - Request - Headers
字段,列出实际请求中会包含的自定义请求头。 -
服务器接收预检请求 :服务器接收到预检请求后,根据自身的 CORS 配置,检查
Origin
以及Access - Control - Request - Method
和Access - Control - Request - Headers
字段,判断是否允许该请求。 -
服务器响应预检请求 :如果允许,服务器会在响应头中添加
Access - Control - Allow - Origin
字段,指定允许的源。同时,还会添加Access - Control - Allow - Methods
字段,列出允许的请求方法;Access - Control - Allow - Headers
字段,列出允许的请求头。此外,还可能有Access - Control - Max - Age
字段,指定预检请求的结果可以被缓存的时长(以秒为单位)。 -
客户端处理预检响应:浏览器接收到预检响应后,检查 CORS 相关头信息。如果预检请求被允许,浏览器会继续发起实际的请求。
-
发起实际请求:客户端按照正常流程发起实际的跨域请求,方法和请求头与预检请求中声明的一致。
-
服务器接收和响应实际请求 :服务器接收实际请求,处理并返回响应,响应头中同样会包含
Access - Control - Allow - Origin
等 CORS 相关头信息,客户端根据这些头信息来决定是否处理响应数据。
在整个过程中,CORS 机制通过请求头和响应头的交互,让服务器能够明确告知浏览器哪些跨域请求是被允许的,从而实现了在遵循同源策略的基础上,安全地进行跨域资源访问。
怎么用CORS实现跨域
以下是使用 CORS(跨域资源共享) 实现跨域的完整方案,涵盖服务端配置、前端请求细节及常见问题处理:
一、CORS 核心机制
CORS 通过服务端设置 HTTP 响应头 ,告知浏览器允许特定页面的跨域请求。浏览器根据响应头决定是否放行跨域请求。
二、服务端配置
1. 基础响应头设置
在服务端响应中添加以下头信息(以 Node.js 为例):
javascript
// Express 中间件示例
app.use((req, res, next) => {
// 允许的源(根据需求替换为具体域名)
res.setHeader('Access-Control-Allow-Origin', 'https://your-frontend.com');
// 允许的请求方法
res.setHeader('Access-Control-Allow-Methods', 'GET, POST, PUT, DELETE, OPTIONS');
// 允许的请求头(需明确列出)
res.setHeader('Access-Control-Allow-Headers', 'Content-Type, Authorization');
// 允许携带 Cookie(如需)
res.setHeader('Access-Control-Allow-Credentials', 'true');
// 预检请求缓存时间(秒)
res.setHeader('Access-Control-Max-Age', '86400');
next();
});
2. 处理预检请求(OPTIONS)
对于非简单请求(如 Content-Type: application/json
),浏览器会先发送 OPTIONS
预检请求:
javascript
// 单独处理 OPTIONS 请求
app.options('*', (req, res) => {
res.sendStatus(200);
});
三、前端请求配置
1. 简单请求(Simple Request)
满足以下条件时,浏览器直接发送请求:
- 方法为
GET
/POST
/HEAD
- 头信息仅含 :
Accept
、Accept-Language
、Content-Language
、Content-Type
(值限于text/plain
、multipart/form-data
、application/x-www-form-urlencoded
)
示例:
javascript
fetch('https://api.example.com/data')
.then(response => response.json())
.then(data => console.log(data));
2. 复杂请求(需预检)
对于其他请求(如 PUT
、自定义头、Content-Type: application/json
):
javascript
fetch('https://api.example.com/data', {
method: 'PUT',
headers: {
'Content-Type': 'application/json',
'Authorization': 'Bearer xxx'
},
body: JSON.stringify({ key: 'value' })
});
3. 携带 Cookie
需前后端协同设置:
javascript
// 前端请求添加 credentials
fetch('https://api.example.com/data', {
credentials: 'include' // 或 'same-origin'
});
// 服务端设置
res.setHeader('Access-Control-Allow-Credentials', 'true');
// 注意:此时 Access-Control-Allow-Origin 不能为 *
五、常见问题与调试
1. 错误排查
- 预检失败 :检查
OPTIONS
响应头是否完整 - 头信息不匹配 :确保
Access-Control-Allow-Headers
包含所有请求头 - Cookie 未携带 :检查前端
credentials
设置和服务端Allow-Credentials
2. 浏览器控制台警告
No 'Access-Control-Allow-Origin' header
:服务端未正确设置允许的源Credentials not supported
:服务端使用Access-Control-Allow-Origin: *
但前端尝试携带 Cookie
3. 工具验证
使用 curl
检查响应头:
bash
curl -I -X OPTIONS https://api.example.com/data
# 检查返回的 CORS 头信息
六、安全最佳实践
- 限制允许的源 :避免使用
Access-Control-Allow-Origin: *
,应指定具体域名 - 缩小方法范围 :按需设置
Access-Control-Allow-Methods
- 验证请求头 :明确列出允许的请求头(避免使用
*
) - 限制缓存时间 :合理设置
Access-Control-Max-Age
(避免过长)
通过以上配置,CORS 可安全高效地实现跨域请求,是现代 Web 应用的首选方案。
Access-Control-Allow-Origin在服务端哪里配置(喜马拉雅)
arduino
resp.res.setHeader("Access-Control-Allow-Origin", "*");
怎么用代理服务器实现跨域
代理服务器通过 将跨域请求转换为同源请求 来解决浏览器的同源策略限制。核心原理是:前端请求同源代理地址,代理服务器转发请求到目标服务器,再将响应返回给前端。以下是具体实现方法及详细步骤:
一、代理服务器工作原理
- 前端请求 :浏览器发送请求到同源的代理路径(如
https://your-site.com/api/data
)。 - 代理转发 :代理服务器接收请求,解析目标地址(如
https://target-api.com/data
),转发请求。 - 响应返回:代理服务器获取目标服务器的响应后,返回给浏览器。
关键优势:
- 绕过浏览器限制:浏览器认为请求是同源的,不会触发跨域拦截。
- 隐藏真实接口 :
保护目标服务器地址,提升安全性
。 - 灵活处理请求:可修改请求头、路径、缓存数据等。
适应场景
代理服务器是解决跨域问题的 无侵入式方案,尤其适合以下场景:
- 无法修改目标服务器配置(如第三方 API)
- 需统一接口前缀或添加全局安全策略
- 兼容旧版浏览器(无需依赖 CORS)
开发阶段 优先使用构建工具自带的代理功能(如 Webpack/Vite),生产环境推荐 Nginx 反向代理。配置时需关注路径重写、请求头传递和安全性策略,确保高效稳定运行。
二、开发环境代理配置
1. Webpack DevServer 代理
适用于 React、Vue 等基于 Webpack 的项目。
配置 webpack.config.js
:
javascript
// webpack.config.js
module.exports = {
devServer: {
proxy: {
'/api': { // 代理路径前缀
target: 'https://target-api.com', // 目标地址
changeOrigin: true, // 修改请求头中的 Host 为目标域名
pathRewrite: {
'^/api': '', // 重写路径(删除 /api 前缀)
},
secure: false, // 允许代理 HTTPS(自签名证书需关闭验证)
}
}
}
};
前端请求示例:
javascript
// 请求到本地开发服务器(同源)
fetch('/api/data').then(...);
// 实际转发到:https://target-api.com/data
代理服务器关键配置项
配置项 | 作用 |
---|---|
target |
目标服务器地址(必填) |
changeOrigin |
修改请求头中的 Host 为目标域名(防目标服务器校验) |
pathRewrite |
重写请求路径(如删除代理前缀 /api ) |
secure |
是否验证 HTTPS 证书(自签名证书需设为 false ) |
proxy_set_header |
设置转发请求头(如传递客户端 IP、Cookie 等) |
2. Vite 代理配置
Vite 项目配置更简洁,修改 vite.config.js
:
javascript
// vite.config.js
export default defineConfig({
server: {
proxy: {
'/api': {
target: 'https://target-api.com',
changeOrigin: true,
rewrite: (path) => path.replace(/^\/api/, ''), // 路径重写
}
}
}
});
三、生产环境代理配置
Node.js 自定义代理服务器
使用 Express 实现灵活代理逻辑:
javascript
const express = require('express');
const axios = require('axios');
const app = express();
// 代理中间件:处理所有 /api 开头的请求
app.use('/api', async (req, res) => {
try {
// 构造目标 URL(去除 /api 前缀)
const targetUrl = `https://target-api.com${req.url.replace(/^\/api/, '')}`;
// 转发请求
const response = await axios({
method: req.method,
url: targetUrl,
data: req.body,
headers: {
// 传递必要请求头(如 Authorization)
...req.headers,
host: 'target-api.com' // 修改 Host 头
}
});
// 返回响应数据
res.status(response.status).send(response.data);
} catch (error) {
// 错误处理
res.status(500).json({ error: 'Proxy request failed' });
}
});
app.listen(3000, () => {
console.log('Proxy server running on port 3000');
});
前端请求 :
http://localhost:3000/api/data
→ Node.js → https://target-api.com/data
四、常见问题与解决方案
1. 代理后接口返回 404
- 检查路径拼接 :确保
proxy_pass
或target
配置正确(注意末尾斜杠)。 - 手动测试目标接口 :使用 Postman 或
curl
直接访问目标地址,验证接口是否可用。
2. 请求头丢失
-
显式传递头信息 :在代理配置中添加
proxy_set_header
(Nginx)或headers
(Node.js)。 -
示例(Nginx) :
nginxproxy_set_header Authorization $http_authorization; proxy_set_header X-Forwarded-For $remote_addr;
3. HTTPS 证书问题
-
关闭证书验证 (仅限测试环境):
javascript// Webpack/Vite secure: false // Node.js process.env.NODE_TLS_REJECT_UNAUTHORIZED = '0'; // 慎用!
4. 性能优化
- 启用缓存 :对静态资源配置缓存策略(如 Nginx 的
proxy_cache
)。 - 负载均衡 :通过 Nginx 的
upstream
分发请求到多个目标服务器。
五、安全注意事项
-
限制代理范围 :
- 避免开放代理(如
proxy_pass http://$http_target;
),防止被滥用转发任意地址。
- 避免开放代理(如
-
IP 白名单 :
-
在 Nginx 中通过
allow/deny
限制访问来源:nginxlocation /api/ { allow 192.168.1.0/24; deny all; proxy_pass ...; }
-
-
监控日志 :
- 定期检查代理服务器的访问日志,识别异常请求。
contentWindow是什么?
contentWindow
是一个 DOM 属性,主要用于操作 <iframe>
或 <frame>
元素,下面为你详细介绍它的相关信息: contentWindow是什么
基本概念
在网页中,<iframe>
和 <frame>
元素用于在当前页面中嵌入另一个 HTML 文档。contentWindow
属性允许你访问这些嵌入文档的 window
对象。window
对象代表浏览器的一个窗口,包含了很多有用的属性和方法,借助 contentWindow
你可以对嵌入文档进行各种操作。
使用场景
你可以通过 contentWindow
属性获取嵌入文档的 window
对象,进而获取其 document
对象,这样就能操作嵌入文档的 DOM 结构了。示例代码如下:
ini
<body>
<iframe id="myIframe" src="https://www.example.com"></iframe>
<script>
const iframe = document.getElementById('myIframe');
const iframeWindow = iframe.contentWindow;
const iframeDocument = iframeWindow.document;
console.log(iframeDocument);
</script>
</body>
怎么用 window.postMessage 实现跨域
使用 window.postMessage
实现跨域的完整指南
window.postMessage
是 HTML5 提供的一种安全的跨域通信机制,允许不同源的窗口(如 iframe
、新标签页、弹出窗口)之间进行数据传递。以下是详细实现步骤及注意事项:
一、核心原理
- 发送方 :通过
postMessage
向目标窗口发送消息。 - 接收方 :监听
message
事件,验证来源后处理消息。 - 安全机制 :通过
origin
校验确保通信双方可信。
二、基本用法
1. 发送消息
javascript
// 发送方代码(源:https://parent.com)
const targetWindow = document.getElementById('my-iframe').contentWindow;
// 发送消息:数据 + 目标源
targetWindow.postMessage(
{ type: 'greeting', data: 'Hello from parent!' },
'https://child.com' // 目标窗口的源(或 '*' 允许任意源)
);
2. 接收消息
javascript
// 接收方代码(源:https://child.com)
window.addEventListener('message', (event) => {
// 验证消息来源
if (event.origin !== 'https://parent.com') return;
// 处理消息
if (event.data.type === 'greeting') {
console.log('Received:', event.data.data);
// 可选:回复消息
event.source.postMessage({ type: 'reply', data: 'Hi back!' }, event.origin);
}
});
三、跨域场景实现
1. 父页面与 iframe 通信
-
父页面(发送方):
html<iframe id="my-iframe" src="https://child.com/page"></iframe> <script> const iframe = document.getElementById('my-iframe'); iframe.onload = () => { iframe.contentWindow.postMessage('Hello', 'https://child.com'); }; </script>
-
子页面(接收方):
javascriptwindow.addEventListener('message', (event) => { if (event.origin === 'https://parent.com') { console.log('Received:', event.data); } });
2. 弹出窗口与原页面通信
-
原页面(打开新窗口):
javascriptconst childWindow = window.open('https://child.com/page'); childWindow.postMessage('Hello', 'https://child.com');
-
新窗口(接收方):
javascriptwindow.addEventListener('message', (event) => { if (event.origin === 'https://parent.com') { console.log('Received:', event.data); // 回复消息 event.source.postMessage('Hi!', event.origin); } });
四、安全注意事项
-
始终验证
event.origin
防止恶意网站伪造消息:
javascriptwindow.addEventListener('message', (event) => { if (event.origin !== 'https://trusted-site.com') return; // 处理消息... });
-
避免使用
'*'
作为目标源明确指定允许的源,减少安全风险:
javascript// 不推荐(允许任意源接收消息) targetWindow.postMessage(data, '*');
-
敏感数据加密
若传递敏感信息,需在前端或服务端加密。
五、高级用法
1. 双向通信
双方互相监听和发送消息:
javascript
// 父页面发送并等待回复
parentWindow.postMessage({ type: 'request', id: 1 }, 'https://child.com');
// 子页面回复
window.addEventListener('message', (event) => {
if (event.data.type === 'request') {
event.source.postMessage({ type: 'response', id: event.data.id, data: '...' }, event.origin);
}
});
2. 结构化克隆算法
postMessage
支持传递复杂对象(函数、DOM 元素除外):
javascript
// 发送复杂数据
targetWindow.postMessage(
{ date: new Date(), array: [1, 2, 3] },
'https://child.com'
);
六、兼容性
- 支持浏览器:所有现代浏览器及 IE8+(IE8 仅支持字符串数据)。
- Polyfill:旧版浏览器无需额外处理,但复杂数据需序列化。
七、常见问题
1. 消息未收到
- 检查目标窗口引用 :确保
targetWindow
正确(如 iframe 是否加载完成)。 - 验证
origin
过滤:确认接收方未错误拦截合法消息。
2. 数据传递失败
- 序列化数据:若传递对象,确保可被结构化克隆算法处理。
- 避免循环引用:对象中不要包含无法序列化的内容(如函数)。
八、总结
window.postMessage
是实现跨域通信的安全、灵活方案,适用于:
- 跨域 iframe/窗口通信:如嵌入第三方组件、多标签页应用。
- 微前端架构:主子应用间传递状态或事件。
- 身份验证:跨域传递 Token 或用户信息(需加密)。
关键步骤:
- 获取目标窗口的引用(如
iframe.contentWindow
)。 - 发送消息时指定目标源。
- 接收方验证
event.origin
后处理数据。
配合合理的源校验和数据加密,可安全实现跨域交互。
怎么用 document.domain 实现跨域
document.domain 是一种实现跨域的方法,不过它的使用有一定的前提条件,即两个页面必须属于同一个基础域名,并且协议和端口要一致。以下为你详细介绍其实现步骤和示例:
实现原理
document.domain
属性可以设置或返回当前文档的域名 。通过将主域相同的不同子域页面的 document.domain
设置为相同的主域名,浏览器会认为这些页面属于同源 ,从而允许它们之间进行一些跨域操作,例如在 <iframe>
嵌套的场景中实现父页面和子页面之间的通信。
实现步骤
1. 确保页面属于同一基础域名
例如,有两个页面:http://sub1.example.com/page1.html
和 http://sub2.example.com/page2.html
,它们的基础域名都是 example.com
,满足使用 document.domain
实现跨域的条件。
2. 在父页面和子页面中设置相同的 document.domain
在父页面和子页面的 JavaScript 代码中,将 document.domain
设置为相同的主域名。
3. 进行跨域操作
在设置好 document.domain
后,就可以进行一些跨域操作,如访问 <iframe>
中的元素、调用 <iframe>
中的函数等。
示例代码
父页面(http://sub1.example.com/page1.html
)
xml
<script>
// 设置 document.domain 为相同的主域名
document.domain = 'example.com';
function callIframeFunction() {
// 获取 iframe 元素
const iframe = document.getElementById('myIframe');
// 获取 iframe 的 window 对象
const iframeWindow = iframe.contentWindow;
// 调用 iframe 中的函数
iframeWindow.childFunction();
}
</script>
<body>
<h1>Parent Page</h1>
<iframe id="myIframe" src="http://sub2.example.com/page2.html"></iframe>
<button onclick="callIframeFunction()">Call Iframe Function</button>
</body>
</html>
子页面(http://sub2.example.com/page2.html
)
xml
<head>
<script>
// 设置 document.domain 为相同的主域名
document.domain = 'example.com';
function childFunction() {
alert('Function in child page is called!');
}
</script>
</head>
<body>
<h1>Child Page</h1>
</body>
替代方案(推荐)
方案 | 适用场景 | 优点 |
---|---|---|
CORS | 任意跨域请求(需服务端支持) | 标准化、安全、支持所有方法 |
postMessage | 跨窗口/iframe 通信 | 灵活、无需服务端配置 |
代理服务器 | 隐藏真实接口地址或绕过浏览器限制 | 无侵入式、兼容性好 |
注意事项
-
端口限制 :使用
document.domain
时,父页面和子页面的端口必须一致。如果端口不同,即使设置了相同的document.domain
,浏览器仍然会认为它们不同源。 -
安全风险 :修改
document.domain
会降低页面的安全性,因为它绕过了部分同源策略的限制。因此,在使用时要谨慎,确保只在可信任的域名之间进行操作。 -
现代替代方案 :优先使用
CORS
或postMessage
,更安全且兼容性强。
怎么用 window.name 实现跨域
window.name
是一个可以在不同页面(即使跨域)之间传递数据的属性,因为在同一个浏览器窗口中,无论页面如何跳转,window.name
的值都不会改变 ,只要浏览器窗口不关闭。以下是使用 window.name
实现跨域的步骤和示例代码:
实现步骤
- 在源页面设置
window.name
:在需要跨域传递数据的源页面中,将要传递的数据赋值给window.name
。 - 创建隐藏的
<iframe>
并加载目标页面 :在源页面中创建一个隐藏的<iframe>
元素,将其src
属性设置为目标页面的地址。由于window.name
在跨域时也能保持值不变,所以在iframe
加载目标页面后,目标页面可以访问到window.name
中存储的数据。 - 在目标页面获取
window.name
中的数据 :在目标页面的 JavaScript 代码中,可以直接通过window.name
获取到源页面设置的数据,从而实现跨域数据传递。
示例代码
以下是一个简单的示例,展示了如何使用 window.name
实现跨域传递数据。
- 源页面(
http://example.com/source.html
)
ini
<body>
<h1>Source Page</h1>
<script>
// 设置 window.name 为要传递的数据
window.name = 'Hello, cross - domain world!';
// 创建一个隐藏的 iframe 并加载目标页面
const iframe = document.createElement('iframe');
iframe.style.display = 'none';
iframe.src = 'http://other.example.com/target.html';
document.body.appendChild(iframe);
</script>
</body>
- 目标页面(
http://other.example.com/target.html
)
xml
<body>
<h1>Target Page</h1>
<script>
// 获取 window.name 中的数据并在控制台输出
console.log(window.name);
</script>
</body>
</html>
在这个示例中,源页面将数据 Hello, cross - domain world!
存储在 window.name
中,然后通过隐藏的 <iframe>
加载目标页面。目标页面加载后,成功获取到了 window.name
中的数据并在控制台输出。
注意事项
window.name
的大小有限制,不同浏览器的限制不同,但一般都能满足大多数场景下的数据传递需求。如果传递的数据过大,可能会导致数据截断或丢失。- 由于
window.name
在整个浏览器窗口中共享,所以在使用时要注意避免与其他页面或脚本设置的window.name
值冲突。可以考虑使用一些独特的命名空间或标识符来区分不同的window.name
值。 - 在某些情况下,可能需要考虑
iframe
的加载时机和状态,以确保数据能够正确传递和获取。例如,可以使用onload
事件来监听iframe
加载完成后再进行数据处理。
window.name 跨域的实际应用案例
-
单点登录系统 :在大型企业或多系统平台中,往往存在多个子系统,它们可能部署在不同的域名下。当用户在一个子系统登录后,希望能在其他子系统中自动登录,而无需再次输入用户名和密码。这时可以利用
window.name
跨域来实现。例如,用户在主系统http://main.example.com
登录后,系统通过iframe
加载各个子系统的登录页面,如http://sub1.example.com/login.html
、http://sub2.example.com/login.html
等。主系统将用户的登录信息存储在window.name
中,子系统的登录页面在加载后可以获取window.name
中的登录信息,然后自动完成登录操作,实现单点登录的效果。 -
跨域数据采集 :一些网站需要采集来自不同域名下的数据,比如统计网站在不同子域名下的用户行为数据。假设网站有
http://www.example.com
、http://blog.example.com
等多个子域名,在各个子域名的页面中通过iframe
加载一个位于主域名下的采集脚本页面http://example.com/collect.html
。子域名页面将用户行为数据通过window.name
传递给collect.html
页面,collect.html
页面再将数据发送到主服务器进行统一的分析和处理,从而实现跨域数据采集。 -
广告投放与跟踪 :广告商通常需要在不同的网站上投放广告,并跟踪广告的展示次数、点击次数等数据。广告商可以将广告代码以
iframe
的形式嵌入到各个合作网站的页面中,广告代码所在的页面将相关数据存储在window.name
中。当用户点击广告或发生其他特定行为时,通过window.name
将数据传递给广告商的跟踪服务器所在的页面(可能与广告代码页面不同域),进而实现跨域的广告效果跟踪和数据统计。
怎么用 location.hash 实现跨域
location.hash
可以实现跨域通信,它主要利用了浏览器在跨域时对 hash
部分的特殊处理,以及页面之间可以通过监听 hash
变化来进行通信的特性 。下面为你详细介绍如何利用 location.hash
实现跨域,以及相关示例代码。
实现原理
- 不同源的页面之间虽然受同源策略限制无法直接通信,但可以通过修改
location.hash
来传递数据。因为hash
的改变不会导致页面的刷新,且可以在不同源的页面之间共享。 - 通常会借助
<iframe>
来实现跨域通信,一个页面作为父页面,另一个页面作为子页面(通过<iframe>
嵌入),通过修改hash
值并监听hashchange
事件来实现数据的传递和接收。
实现步骤和示例代码
1. 父页面向子页面传递数据
以下是父页面的示例代码(parent.html
):
xml
<body>
<h1>Parent Page</h1>
<!-- 创建一个 iframe 并加载子页面 -->
<iframe id="childFrame" src="http://child.example.com/child.html"></iframe>
<button onclick="sendDataToChild()">Send Data to Child</button>
<script>
function sendDataToChild() {
const childFrame = document.getElementById('childFrame');
// 修改 iframe 的 src,添加 hash 值传递数据
childFrame.src = childFrame.src.split('#')[0] + '#data=Hello from parent';
}
</script>
</body>
</html>
以下是子页面的示例代码(child.html
):
xml
<body>
<h1>Child Page</h1>
<script>
// 监听 hashchange 事件
window.addEventListener('hashchange', function () {
const hash = window.location.hash;
if (hash) {
// 解析 hash 中的数据
const data = hash.split('=')[1];
console.log('Received data from parent: ', data);
}
});
</script>
</body>
</html>
在上述代码中,父页面通过修改 iframe
的 src
,将数据以 hash
的形式传递给子页面。子页面监听 hashchange
事件 ,当 hash
发生变化时,解析出其中的数据。
2. 子页面向父页面传递数据
xml
<!DOCTYPE html>
<body>
<h1>Child Page</h1>
<button onclick="sendDataToParent()">Send Data to Parent</button>
<script>
function sendDataToParent() {
// 修改父页面的 hash 值传递数据
window.parent.location.hash = 'data=Hello from child';
}
</script>
</body>
</html>
以下是父页面的示例代码(parent.html
):
xml
<body>
<h1>Parent Page</h1>
<iframe id="childFrame" src="http://child.example.com/child.html"></iframe>
<script>
// 监听 hashchange 事件
window.addEventListener('hashchange', function () {
const hash = window.location.hash;
if (hash) {
// 解析 hash 中的数据
const data = hash.split('=')[1];
console.log('Received data from child: ', data);
}
});
</script>
</body>
</html>
在这个例子中,子页面通过 window.parent.location.hash
修改父页面的 hash
值,将数据传递给父页面。父页面监听 hashchange
事件,解析出其中的数据。
注意事项
- 数据量限制 :
hash
部分的长度是有限制的,因此传递的数据量不能太大,否则可能会导致数据丢失。 - URL 编码 :如果传递的数据包含特殊字符,需要进行 URL 编码,以避免出现问题。可以使用
encodeURIComponent
和decodeURIComponent
方法进行编码和解码。 - 兼容性 :虽然大多数现代浏览器都支持
hashchange
事件,但在一些旧版本的浏览器中可能需要进行兼容性处理。
怎么用 websocket 实现跨域
使用 WebSocket 实现跨域的完整指南
WebSocket 是一种 全双工通信协议 ,其设计天然支持跨域通信 。浏览器在建立 WebSocket 连接时,会通过 HTTP 握手请求升级协议,且不受同源策略限制。以下是具体实现方法及注意事项:
一、WebSocket 跨域原理
- 协议升级机制 :
WebSocket 在初始握手阶段使用 HTTP 请求,请求头中包含Origin
字段,但浏览器允许跨域 WebSocket 连接。 - 服务端控制 :
服务端需验证Origin
头,决定是否允许连接(防止恶意请求)。
二、实现步骤
1. 前端代码(浏览器端)
javascript
// 创建 WebSocket 连接(支持跨域)
const socket = new WebSocket('wss://api.example.com/ws');
// 监听连接建立
socket.onopen = () => {
console.log('WebSocket connected');
socket.send('Hello from client!');
};
// 接收消息
socket.onmessage = (event) => {
console.log('Received:', event.data);
};
// 处理错误
socket.onerror = (error) => {
console.error('WebSocket error:', error);
};
// 关闭连接
socket.onclose = () => {
console.log('WebSocket closed');
};
2. 服务端代码(Node.js + ws 库)
bash
# 安装 ws 库
npm install ws
javascript
const WebSocket = require('ws');
const wss = new WebSocket.Server({ port: 8080 });
wss.on('connection', (ws, req) => {
// 验证 Origin(可选,但建议)
const origin = req.headers.origin;
if (origin !== 'https://your-site.com') {
ws.close(); // 拒绝非指定源的连接
return;
}
// 接收客户端消息
ws.on('message', (message) => {
console.log('Received:', message.toString());
// 发送响应
ws.send('Hello from server!');
});
});
console.log('WebSocket server running on ws://localhost:8080');
三、跨域关键配置
1. 服务端 Origin 验证
在生产环境中,必须验证 Origin
头以防止 CSWSH(跨站 WebSocket 劫持)攻击:
javascript
wss.on('headers', (headers, req) => {
// 允许指定源的连接(可选设置 CORS 头)
if (req.headers.origin === 'https://your-site.com') {
headers.push('Access-Control-Allow-Origin: https://your-site.com');
}
});
2. 使用安全的 WebSocket(wss://)
通过 SSL/TLS 加密通信,防止数据被窃听:
javascript
// 使用 HTTPS 服务器(示例代码)
const https = require('https');
const fs = require('fs');
const server = https.createServer({
cert: fs.readFileSync('server.crt'),
key: fs.readFileSync('server.key')
});
const wss = new WebSocket.Server({ server });
server.listen(443);
四、WebSocket 协议特点
特性 | 说明 |
---|---|
跨域支持 | 无需 CORS 配置,浏览器默认允许跨域 WebSocket 连接 |
实时双向通信 | 支持客户端与服务器主动推送消息 |
低延迟 | 相比 HTTP 轮询,减少不必要的网络开销 |
协议升级 | 握手阶段使用 HTTP,后续通信使用 WebSocket 二进制帧 |
五、注意事项
-
Origin 验证 :
服务端必须验证Origin
头,防止恶意网站通过 WebSocket 攻击用户会话。 -
心跳机制 :
添加心跳包(定期发送 Ping/Pong)检测连接状态,避免死连接。javascript// 服务端发送心跳 setInterval(() => { wss.clients.forEach((client) => { if (client.readyState === WebSocket.OPEN) { client.ping(); } }); }, 30000);
-
浏览器兼容性 :
所有现代浏览器均支持 WebSocket,但需处理旧版浏览器降级方案(如 SSE 或轮询)。
六、与其他跨域方案对比
方案 | 适用场景 | 优点 |
---|---|---|
WebSocket | 实时双向通信(如聊天、游戏) | 低延迟、原生跨域支持 |
CORS | RESTful API 请求 | 标准化、支持所有 HTTP 方法 |
SSE | 服务器单向推送(如股票行情) | 简单、浏览器自动重连 |
JSONP | 旧项目兼容性 | 无需服务端改动 |
七、总结
- WebSocket 是跨域实时通信的最佳选择:适用于需高频双向数据交互的场景(如在线游戏、即时通讯)。
- 安全性优先 :始终验证
Origin
头并使用wss://
加密连接。 - 服务端实现灵活:可使用 Node.js、Java、Python 等语言的 WebSocket 库。
通过合理配置 WebSocket,可轻松实现高效、安全的跨域实时通信。
怎么用 NodeJS 中间件实现跨域
完整工作流程图解
[浏览器] -- 跨域请求 --> [Node.js 中间件]
① 检查 Origin 是否在白名单
② 设置 CORS 响应头
③ 处理 OPTIONS 预检
④ 转发请求到业务路由
⑤ 返回数据给浏览器
通过以上配置,可实现灵活安全的跨域控制,建议优先使用 cors
库简化开发,生产环境配合反向代理提升性能。
说一说创建 Ajax 的大致过程
(1)创建 XMLHttpRequest 对象,也就是创建一个异步调用对象 。
(2)设置响应 HTTP 请求状态变化的函数 。
(3)打开一个新的 HTTP 请求 ,并指定该 HTTP 请求的方法、URL 及验证信息。
(4)发送 HTTP 请求。
(5)在响应回调函数中,根据改变状态和请求状态码,获取异步请求返回的数据。
(6)渲染返回的数据。

AJAX原理
Ajax(Asynchronous JavaScript And XML),异步 JavaScript 和 XML ,用于异步请求数据,在不刷新网页的情况下更新页面数据,提升用户体验
怎么实现ajax
- XMLHttpRequest
- fetch(注意兼容性使用can i use)
AJAX 的优点是什么?
(1)最大的优点是页面无刷新更新 ,用户的体验非常好。
(2)使用异步方式与服务器通信 ,具有更迅速的响应能力。
(3)可以将一些服务器工作转移到客户端,利用客户端资源来处理,减轻服务器和带宽的压力,节约空间和带宽租用成本。Ajax 的原则是 "按需获取数据",可以最大限度地减少冗余请求及响应对服务器造成的负担 。
(4)技术标准化,并被浏览器广泛支持,不需要下载插件或者小程序。
(5)AJAX 可使因特网应用程序更小、更快、更友好。
AJAX 的缺点是什么?
(1)Ajax 不支持浏览器 back 按钮。
(2)有安全问题,Ajax 暴露了与服务器交互的细节。
(3)对搜索引擎不友好。
(4)破坏了程序的异常机制。
(5)不容易调试。
AJAX 如何处理跨域 / 写一个AJAX跨域(兑吧)
以下是使用 AJAX 实现跨域请求 的两种常用方法示例,包含 CORS(推荐) 和 JSONP(传统) 的实现代码:
方法一:使用 CORS 实现跨域(推荐)
前端代码(JavaScript)
javascript
// 使用 Fetch API(现代浏览器)
fetch('https://api.example.com/data', {
method: 'GET',
headers: {
'Content-Type': 'application/json',
},
credentials: 'include' // 允许携带 Cookie(需服务端配合)
})
.then(response => response.json())
.then(data => console.log('成功获取数据:', data))
.catch(error => console.error('请求失败:', error));
// 或使用 XMLHttpRequest(兼容旧浏览器)
const xhr = new XMLHttpRequest();
xhr.open('GET', 'https://api.example.com/data');
xhr.withCredentials = true; // 允许携带 Cookie
xhr.onreadystatechange = function() {
if (xhr.readyState === 4 && xhr.status === 200) {
console.log('成功获取数据:', JSON.parse(xhr.responseText));
}
};
xhr.send();
服务端代码(Node.js + Express)
javascript
const express = require('express');
const app = express();
// CORS 中间件配置
app.use((req, res, next) => {
const allowedOrigins = ['https://your-frontend.com', 'http://localhost:3000'];
const origin = req.headers.origin;
if (allowedOrigins.includes(origin)) {
res.setHeader('Access-Control-Allow-Origin', origin);
}
res.setHeader('Access-Control-Allow-Methods', 'GET, POST, OPTIONS');
res.setHeader('Access-Control-Allow-Headers', 'Content-Type, Authorization');
res.setHeader('Access-Control-Allow-Credentials', 'true'); // 允许 Cookie
res.setHeader('Access-Control-Max-Age', '86400');
if (req.method === 'OPTIONS') return res.sendStatus(200); // 处理预检请求
next();
});
// 数据接口
app.get('/data', (req, res) => {
res.json({ message: '跨域数据获取成功!' });
});
app.listen(3000, () => console.log('服务端运行在 http://localhost:3000'));
方法二:使用 JSONP 实现跨域(传统方法)
前端代码
javascript
// 定义回调函数
function handleResponse(data) {
console.log('收到数据:', data);
}
// 动态创建 <script> 标签
const script = document.createElement('script');
script.src = 'https://api.example.com/data?callback=handleResponse';
document.body.appendChild(script);
// 错误处理(可选)
script.onerror = () => console.error('JSONP 请求失败');
服务端代码(Node.js)
javascript
const http = require('http');
const url = require('url');
const server = http.createServer((req, res) => {
const parsedUrl = url.parse(req.url, true);
const callbackName = parsedUrl.query.callback;
const data = { message: 'Hello from JSONP!' };
// 返回回调函数调用
res.writeHead(200, { 'Content-Type': 'application/javascript' });
res.end(`${callbackName}(${JSON.stringify(data)})`);
});
server.listen(3000, () => console.log('JSONP 服务端运行中'));
关键区别与注意事项
特性 | CORS | JSONP |
---|---|---|
请求类型 | 支持所有 HTTP 方法 | 仅 GET |
安全性 | 高(服务端可控) | 低(依赖第三方脚本) |
错误处理 | 可通过 Fetch/XHR 捕获 | 只能通过 onerror 事件 |
数据格式 | 支持 JSON、文本、二进制等 | 仅 JSON(包裹在函数中) |
兼容性 | 现代浏览器(IE10+) | 全浏览器(包括 IE6) |
常见问题解决
-
预检请求失败(CORS)
- 确保服务端正确处理
OPTIONS
方法并返回正确的头信息。
- 确保服务端正确处理
-
JSONP 回调未执行
- 检查服务端返回的数据格式是否为
callbackName(data)
。
- 检查服务端返回的数据格式是否为
-
Cookie 未携带(CORS)
- 前端设置
credentials: 'include'
,服务端设置Access-Control-Allow-Credentials: true
且Access-Control-Allow-Origin
不能为*
。
- 前端设置
总结
- 推荐方案 :优先使用 CORS,它是现代 Web 开发的标准跨域方案,安全且功能完整。
- 备用方案 :仅在需要兼容老旧浏览器且无法修改服务端时使用 JSONP。
- 安全提示 :生产环境中务必限制允许的源(如
Access-Control-Allow-Origin
不要设为*
)。
AJAX 发生跨域要设置什么(沪江)
resp.setHeader("Access-Control-Allow-Origin", "*");
AJAX 请求如何把异步改为同步?
XMLHttpRequest
是浏览器内置对象,能用来创建 HTTP 请求。默认情况下请求是异步的 ,但你可以通过设置 open()
方法的第三个参数 为 false
来把它改成同步请求。示例代码如下:
javascript
// 创建 XMLHttpRequest 实例
const xhr = new XMLHttpRequest();
// 配置请求,第三个参数 false 表示同步请求
xhr.open('GET', 'https://example.com/api/data', false);
// 发送请求
xhr.send();
// 根据状态码判断请求是否成功
if (xhr.status === 200) {
console.log('请求成功,响应内容为:', xhr.responseText);
} else {
console.error('请求失败,状态码为:', xhr.status);
}
需要注意,在现代浏览器中,同步的 XMLHttpRequest
请求会阻塞页面渲染和用户交互,导致页面无响应,所以不推荐在浏览器环境里使用。
表单可以跨域吗(网易)
可以的,因为原页面用 form 提交到另一个域名之后,原页面的脚本无法获取新页面中的内容。所以浏览器认为这是安全的 。而 AJAX 是可以读取响应内容的,因此浏览器不能允许你这样做,请求能正常发送到服务端并返回结果,但浏览器会拦截响应 。如果你细心的话你会发现,其实请求已经发送出去了,你只是拿不到响应而已。所以浏览器这个策略的本质是,一个域名的 JS ,在未经允许的情况下,不得读取另一个域名的内容。但浏览器并不阻止你向另一个域名发送请求 ,这就是浏览器的同源策略。
表单提交后页面跳转,无法读取响应;AJAX 会获取响应内容,浏览器认为不安全,会拦截响应。
1. 传统表单提交(同步跳转)
- 可以跨域 :浏览器允许表单(
<form>
)直接向不同域的服务器提交数据(如 POST 请求)。 - 限制 :
- 无法通过 JavaScript 读取响应:若表单提交后跳转到跨域页面,前端无法通过 JS 获取响应内容(受同源策略限制)。
- 页面会跳转:提交后浏览器会加载新页面,无法停留在当前页。
示例:
html
<form action="https://api.other-domain.com/submit" method="POST">
<input type="text" name="username">
<input type="submit" value="提交">
</form>
2. AJAX 表单提交(异步请求)
- 默认禁止跨域:若使用 JavaScript(如 Fetch/XHR)异步提交表单到不同域,浏览器会拦截请求(触发 CORS 拦截)。
- 解决方案 :
- 服务端配置 CORS :目标服务器需返回
Access-Control-Allow-Origin
等响应头。 - 使用代理服务器:前端将请求发送到同源代理,由代理转发到目标服务器。
- 服务端配置 CORS :目标服务器需返回
示例(CORS):
javascript
// 前端代码
fetch('https://api.other-domain.com/submit', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ username: 'John' }),
credentials: 'include' // 允许携带 Cookie
});
// 服务端需配置 CORS 头
3. 安全注意事项
- CSRF 攻击风险:表单跨域提交可能被恶意网站利用,发起跨站请求伪造攻击。
- 防护措施 :
- CSRF Token:服务端生成唯一 Token,表单提交时校验。
- SameSite Cookie :设置 Cookie 的
SameSite
属性为Lax
或Strict
。 - 验证 Referer:服务端检查请求来源是否合法。
总结
场景 | 是否允许跨域 | 能否读取响应 | 是否需要 CORS |
---|---|---|---|
传统表单同步提交 | ✅ 允许 | ❌ 无法通过 JS 读取 | 不需要 |
AJAX 异步提交 | ❌ 默认拦截 | ✅ 需服务端配置 CORS | 需要 |
实际开发建议:
- 若需异步处理跨域表单数据,优先使用 CORS 方案。
- 若为简单数据提交(无需处理响应),可直接用传统表单,但需防范 CSRF。
Web 应用从服务器主动推送 Data 到客户端有哪些方式?
-
Ajax 轮询,即定期发送请求,获取数据。
-
Commet,即基于 HTTP 长连接的服务器推送技术。
-
XHR 长轮询,即服务器端定期返回数据,客户端接收数据,并再次发送请求。
-
WebSocket,即基于 Socket 协议实现数据的推送。
-
SSE(Server-Send Event),即允许网页获取来自服务器端
Web 应用中实现服务器主动向客户端推送数据的常用方法如下,按技术特点和适用场景分类:
一、实时双向通信(全双工)
1. WebSocket
-
原理:基于 TCP 的独立协议,提供全双工通信通道。
-
特点 :
- 双向实时通信:服务端和客户端均可主动发送消息。
- 低延迟:适合高频交互场景(如聊天、游戏)。
- 需独立协议 :使用
ws://
或wss://
(加密)。
-
实现 :
javascript// 客户端 const socket = new WebSocket('wss://api.example.com/ws'); socket.onmessage = (event) => { console.log('收到消息:', event.data); }; // 服务端(Node.js + ws 库) const WebSocket = require('ws'); const wss = new WebSocket.Server({ port: 8080 }); wss.on('connection', (ws) => { ws.send('欢迎连接!'); });
二、单向服务器推送
2. Server-Sent Events (SSE)
-
原理:基于 HTTP 长连接,服务端通过流式传输推送数据。
-
特点 :
- 单向推送:仅服务端→客户端。
- 自动重连:浏览器内置断线重试机制。
- 简单轻量:使用标准 HTTP,无需额外协议。
-
实现 :
javascript// 客户端 const eventSource = new EventSource('/sse'); eventSource.onmessage = (e) => { console.log('数据:', e.data); }; // 服务端(Node.js) app.get('/sse', (req, res) => { res.setHeader('Content-Type', 'text/event-stream'); setInterval(() => { res.write(`data: ${new Date().toISOString()}\n\n`); // 数据格式要求 }, 1000); });
三、模拟推送(非真正主动)
3. 长轮询(Long Polling)
-
原理:客户端发起请求,服务端持有连接直至有数据返回。
-
特点 :
- 兼容性好:支持所有浏览器。
- 高延迟:每次需重建连接,性能较差。
-
实现 :
javascript// 客户端 function poll() { fetch('/data') .then(res => res.json()) .then(data => { console.log(data); poll(); // 递归调用 }); } poll(); // 服务端(需延迟响应) app.get('/data', async (req, res) => { const data = await waitForNewData(); // 阻塞直到有新数据 res.json(data); });
4. HTTP/2 Server Push
-
原理:HTTP/2 允许服务器主动推送资源(如 CSS/JS 文件)。
-
特点 :
- 资源预加载:提升页面加载速度。
- 不适用动态数据:无法推送业务数据(如聊天消息)。
-
配置 (Nginx):
nginxlocation / { http2_push /style.css; }
四、第三方库与框架
5. Socket.IO
-
原理:封装 WebSocket 并提供降级方案(如轮询)。
-
特点 :
- 自动兼容:支持旧版浏览器。
- 扩展功能:房间管理、广播等。
-
实现 :
javascript// 客户端 const socket = io('https://api.example.com'); socket.on('message', (data) => { console.log(data); }); // 服务端(Node.js) const io = require('socket.io')(3000); io.on('connection', (socket) => { socket.emit('message', 'Hello!'); });
五、方案对比与选型建议
方案 | 实时性 | 方向 | 协议 | 适用场景 |
---|---|---|---|---|
WebSocket | 极高 | 双向 | WS/WSS | 实时聊天、协作编辑 |
SSE | 高 | 单向 | HTTP | 股票行情、新闻推送 |
长轮询 | 低 | 模拟双向 | HTTP | 兼容性要求高的旧系统 |
HTTP/2 Push | 无 | 资源预推送 | HTTP/2 | 静态资源加速 |
Socket.IO | 高 | 双向 | WS+降级 | 需兼容旧浏览器的实时应用 |
六、关键考虑因素
- 浏览器兼容性 :
- WebSocket 需 IE10+,SSE 需 IE 除外的主流浏览器。
- 数据频率与量级 :
- 高频小数据(如游戏指令)选 WebSocket,低频大数据(如日志)选 SSE。
- 服务端支持 :
- WebSocket 需支持 WS 协议的服务器(如 Node.js、Java Netty)。
- 安全性 :
- 生产环境使用
wss://
和 HTTPS,防止中间人攻击。
- 生产环境使用
通过合理选择技术方案,可高效实现服务器到客户端的主动数据推送,满足不同业务场景的需求。
基础面试题
以下是跨域相关的常见面试题及详细解答,结合技术原理、解决方案和实际应用场景,帮助你在面试中全面展示知识深度和广度:
一、跨域基础
1. 什么是跨域?
跨域(Cross-Origin)指浏览器因同源策略(Same-Origin Policy)限制,阻止网页向不同源(协议、域名、端口任一不同)的服务器发送请求或接收响应。例如:
http://a.com
向https://a.com
发请求(协议不同)http://a.com
向http://b.com
发请求(域名不同)。
2. 为什么浏览器限制跨域?
- 安全防护:防止恶意网站通过脚本窃取用户数据(如 Cookie、LocalStorage)或发起 CSRF/XSS 攻击。
- 数据隔离:确保不同源的资源相互独立,避免数据污染。
3. 哪些标签允许跨域加载资源?
<img>
、<script>
、<link>
、<iframe>
等标签可跨域加载资源,但 JavaScript 无法直接读取内容。
二、跨域解决方案
1. CORS(跨域资源共享)
-
原理 :服务端通过设置 HTTP 响应头(如
Access-Control-Allow-Origin
)声明允许的源。 -
适用场景:主流方案,支持所有 HTTP 方法,需服务端配合。
-
关键配置 :
httpAccess-Control-Allow-Origin: https://your-site.com # 允许指定源 Access-Control-Allow-Methods: GET, POST, PUT # 允许的方法 Access-Control-Allow-Credentials: true # 允许携带 Cookie
-
预检请求 :非简单请求(如
Content-Type: application/json
)会先发送OPTIONS
请求验证权限。
2. JSONP
-
原理 :利用
<script>
标签的跨域特性,通过回调函数接收数据。 -
示例 :
javascriptfunction handleData(data) { console.log(data); } const script = document.createElement('script'); script.src = 'https://api.com/data?callback=handleData'; document.body.appendChild(script);
-
缺点:仅支持 GET 请求,存在 XSS 风险。
3. 反向代理
-
原理 :通过同源代理服务器转发请求,如 Nginx 配置:
nginxlocation /api { proxy_pass http://target-server; add_header Access-Control-Allow-Origin *; }
-
适用场景:生产环境部署,隐藏真实接口地址。
4. WebSocket
-
原理:WebSocket 协议天然支持跨域,适用于实时双向通信(如聊天室)。
-
示例 :
javascriptconst socket = new WebSocket('wss://api.com/ws'); socket.onmessage = (event) => { console.log(event.data); };
-
优点:低延迟,全双工通信。
5. postMessage
-
原理:HTML5 API,跨窗口通信(如 iframe 与父页面)。
-
示例 :
javascript// 父页面发送消息 iframe.contentWindow.postMessage('Hello', 'https://child.com'); // 子页面接收消息 window.addEventListener('message', (event) => { if (event.origin === 'https://parent.com') console.log(event.data); });
-
适用场景:跨域页面可控通信。
三、高频面试题
1. 跨域请求是否真正发送?
- 答案 :请求能正常发送到服务端并返回结果,但浏览器会拦截响应。
2. 为什么表单可跨域提交,而 AJAX 不行?
- 答案:表单提交后页面跳转,无法读取响应;AJAX 会获取响应内容,浏览器认为不安全,会拦截响应。
3. 如何携带 Cookie 跨域?
- 前端 :设置
withCredentials: true
(Fetch API)或xhr.withCredentials = true
(XHR)。 - 服务端 :设置
Access-Control-Allow-Credentials: true
且Access-Control-Allow-Origin
不能为*
。
五、实战经验分享
- 开发环境 :使用 Webpack/Vite 代理解决跨域,配置
devServer.proxy
。 - 生产环境:优先使用 Nginx 反向代理或 CORS,避免 JSONP 的安全隐患。
- 安全优化 :限制
Access-Control-Allow-Origin
为具体域名,避免使用*
。
通过掌握以上知识点,结合具体场景灵活选择解决方案,可全面应对跨域相关的技术挑战。