
浏览器同源策略禁止不同域名、协议或端口的页面直接读写彼此的 localstorage;本文详解基于 postmessage + 同源中继页的纯前端跨域存储方案,并提供可运行代码与关键注意事项。
浏览器同源策略禁止不同域名、协议或端口的页面直接读写彼此的 localstorage;本文详解基于 postmessage + 同源中继页的纯前端跨域存储方案,并提供可运行代码与关键注意事项。
在 Web 开发中,常有类似需求:A 应用(如 https://cs.money)需将数据存入本地存储,B 应用(如 https://tynachofinder.ge/finder/)需在另一上下文中读取该数据。但直接调用 localStorage.setItem() 与 localStorage.getItem() 在跨域环境下必然失败——因为 localStorage 严格遵循同源策略(Same-Origin Policy):只有当协议(protocol)、域名(host)和端口(port)三者完全一致时,才能共享同一份存储空间。这意味着 https://a.com 和 https://b.com 的 localStorage 完全隔离,互不可见,返回值恒为 null。
✅ 推荐方案:postMessage + 同源中继页(Bridge Page)
该方案无需后端参与、不依赖 Cookie 或服务器代理,纯前端实现,安全性可控,是目前最成熟可靠的跨域 localStorage 共享方式。
▪ 步骤一:部署一个可信的同源中继页(如 https://cs.money/storage-bridge.html)
此页面作为“信任枢纽”,托管在双方均可访问的任一主域下(推荐选业务主域,如 cs.money),仅负责中转消息与操作本域 localStorage:
<!-- https://cs.money/storage-bridge.html -->
<meta charset="utf-8"><script>
const TRUSTED_ORIGINS = [
'https://cs.money',
'https://tynachofinder.ge'
];
window.addEventListener('message', (e) => {
// 1. 严格校验来源,防止恶意调用
if (!TRUSTED_ORIGINS.includes(e.origin)) {
console.warn('Blocked untrusted origin:', e.origin);
return;
}
const { action, key, value } = e.data;
try {
switch (action) {
case 'set':
localStorage.setItem(key, String(value));
e.source.postMessage({ status: 'ok', msg: 'set success' }, e.origin);
break;
case 'get':
const result = localStorage.getItem(key);
e.source.postMessage({ status: 'ok', value: result }, e.origin);
break;
case 'remove':
localStorage.removeItem(key);
e.source.postMessage({ status: 'ok', msg: 'removed' }, e.origin);
break;
default:
e.source.postMessage({ status: 'error', msg: 'unknown action' }, e.origin);
}
} catch (err) {
e.source.postMessage({ status: 'error', msg: err.message }, e.origin);
}
});
</script>
? 安全提示:务必显式声明 TRUSTED_ORIGINS 并校验 e.origin,禁用通配符 *;避免因 XSS 漏洞导致 localStorage 被恶意篡改。
▪ 步骤二:在发起方(如 cs.money)写入数据
// 在 https://cs.money 页面中执行
function saveToBridge(key, value) {
const iframe = document.createElement('iframe');
iframe.src = 'https://cs.money/storage-bridge.html';
iframe.style.display = 'none';
document.body.appendChild(iframe);
iframe.addEventListener('load', () => {
// 确保 iframe 加载完成后再发送消息
iframe.contentWindow.postMessage(
{ action: 'set', key, value },
'https://cs.money' // 目标 origin 必须精确匹配
);
});
// 可选:监听成功响应
window.addEventListener('message', (e) => {
if (e.origin === 'https://cs.money' && e.data.status === 'ok') {
console.log('✅ Data saved via bridge:', e.data.msg);
document.body.removeChild(iframe); // 清理 iframe
}
}, { once: true });
}
// 示例调用
saveToBridge('myurl', 'This is Session');
▪ 步骤三:在接收方(如 tynachofinder.ge)读取数据
// 在 https://tynachofinder.ge/finder/ 页面中执行
async function loadFromBridge(key) {
return new Promise((resolve, reject) => {
const iframe = document.createElement('iframe');
iframe.src = 'https://cs.money/storage-bridge.html';
iframe.style.display = 'none';
document.body.appendChild(iframe);
const onMessage = (e) => {
if (e.origin !== 'https://cs.money') return;
if (e.data.status === 'ok') {
resolve(e.data.value);
} else {
reject(new Error(e.data.msg || 'Bridge operation failed'));
}
// 清理
window.removeEventListener('message', onMessage);
document.body.removeChild(iframe);
};
window.addEventListener('message', onMessage);
// 发起读取请求
iframe.addEventListener('load', () => {
iframe.contentWindow.postMessage(
{ action: 'get', key },
'https://cs.money'
);
}, { once: true });
});
}
// 使用示例
loadFromBridge('myurl')
.then(data => console.log('? Retrieved:', data)) // 输出:"This is Session"
.catch(err => console.error('❌ Failed:', err));
⚠️ 关键注意事项
- iframe 必须显式加载完成:务必监听 iframe.onload,不可在 appendChild 后立即 postMessage,否则 contentWindow 可能为 null;
- origin 字符串必须精确匹配:postMessage(targetOrigin) 中的 targetOrigin 参数不可省略或设为 '*',否则浏览器会静默丢弃消息;
- localStorage 数据类型限制:所有值均被强制转为字符串存储;若需存对象,请先 JSON.stringify(),读取后 JSON.parse();
- 无持久化保障:该方案依赖中继页所在域的 localStorage;若用户清除 cs.money 的缓存,数据即丢失;
- 不适用于敏感凭证:切勿用此方式传递 token、密码等高敏信息;建议配合服务端签名校验或短期有效期设计。
✅ 替代方案简评(供参考)
| 方案 | 是否可行 | 说明 |
|---|---|---|
| document.domain 设置 | ❌ 不适用 | 仅对子域间 Cookie 共享有效(如 a.example.com ↔ b.example.com),对 localStorage 无效 |
| CORS / 服务端代理 | ⚠️ 可行但非本题解法 | 需后端配合,本质是绕过前端限制,不属于“纯 localStorage 跨应用”范畴 |
| IndexedDB + postMessage | ✅ 技术可行 | 但复杂度显著上升,且仍受同源限制,需同样依赖中继页,无明显优势 |
综上,postMessage + 同源中继页是当前唯一稳定、安全、零服务端依赖的跨域 localStorage 共享方案。它将“跨域读写”转化为“同域中转”,既尊重浏览器安全模型,又满足实际业务诉求。只要严格校验 origin、合理管理 iframe 生命周期,并做好错误降级处理,即可在生产环境可靠落地。











