JSONP 与跨域通道


文档摘要

6.4 JSONP 与跨域通道 6.1 节说过,同源策略拦的是"读取响应"而不是"发出请求"。这个细节留了一道缝,本章最后一节就来讲这道缝里长出来的技术——JSONP:把数据包装成脚本、借脚本标签不受同源限制的便车过境。它是一代人的跨域解法,也是一代人的安全教材。本节讲清它的原理与局限,再走一遍现代的正路(CORS),最后把散落各节的安全底线一次收拢。 本节摘要:JSONP 的机制是"约定回调名 + 脚本标签取数":服务端把 JSON 包进函数调用返回,浏览器当脚本执行即完成交付;它只能发 GET、错误无处安放、且执行来路不明的代码,注定是过渡方案。现代正路是 CORS 响应头放行,原生 fetch 与 都能直连。

6.4 JSONP 与跨域通道

6.1 节说过,同源策略拦的是"读取响应"而不是"发出请求"。这个细节留了一道缝,本章最后一节就来讲这道缝里长出来的技术——JSONP:把数据包装成脚本、借脚本标签不受同源限制的便车过境。它是一代人的跨域解法,也是一代人的安全教材。本节讲清它的原理与局限,再走一遍现代的正路(CORS),最后把散落各节的安全底线一次收拢。

本节摘要:JSONP 的机制是"约定回调名 + 脚本标签取数":服务端把 JSON 包进函数调用返回,浏览器当脚本执行即完成交付;它只能发 GET、错误无处安放、且执行来路不明的代码,注定是过渡方案。现代正路是 CORS 响应头放行,原生 fetch 与 $.ajax 都能直连。

原理:把数据伪装成脚本

普通 AJAX 取回的是数据,脚本标签加载的是代码——这就是 JSONP 全部的巧思。服务端不返回裸 JSON,而是返回一次函数调用,函数名与前端约定:

// 前端:先在全局备好接收函数 function onQueueArrive(data) { console.log('收到队列:', data.length); } // 再动态造一个脚本标签去"加载"数据 var s = document.createElement('script'); s.src = 'api/queue?callback=onQueueArrive'; document.body.appendChild(s);
// 服务端实际返回的"正文"是一行可执行代码 onQueueArrive([{ name: '张三' }, { name: '李四' }]);

浏览器的执行流:脚本标签加载这个地址,把响应当 JS 代码执行,恰好调用约定好的函数——数据就这样穿过了同源的门。jQuery 把这套仪式封装进 $.ajax:配置 dataType: 'jsonp' 并给出回调名,其余与普通请求无异。

$.ajax({ url: 'api/queue', dataType: 'jsonp', jsonpCallback: 'onQueueArrive', // 也可不指定,让 jQuery 自动生成临时函数名 success: function (data) { renderQueue(data); } });

图:JSONP 与 CORS 两条过境通道

图:JSONP 与 CORS 两条过境通道

局限与风险:为什么它是过渡方案

JSONP 的三宗局限都源于"借道"这个出身。其一,只能 GET——脚本标签天生只会发起 GET,提交类需求无能为力。其二,错误无处安放:脚本加载失败既不走 error 回调也不抛出可捕获的异常,超时检测要自己造临时定时器。其三,也是最重的一条:它在页面上执行对方返回的代码。回调函数名是前端拼进地址的,若把用户输入直接拼进去,攻击者可以构造地址执行任意脚本;即使名字可控,服务端一旦被攻破,返回的"数据"也可以是任意恶意代码。所以老规矩必须立住:回调名绝不拼接用户输入,JSONP 只用于完全可信的数据源

CORS:现代的正路

跨域的正解叫 CORS(跨源资源共享),机制朴素:服务端在响应头里声明允许谁读,浏览器校验通过即放行。对前端来说,服务端配好之后什么都不用做——普通请求直接发:

$.ajax({ url: 'https://api.example-partner.com/queue', dataType: 'json', success: renderQueue }); // 服务端放行即可,前端无特殊配置 fetch('https://api.example-partner.com/queue') .then(function (r) { return r.json(); }) .then(renderQueue);

要点有两条。其一,带凭据(Cookie)的跨域请求需要显式配置,$.ajaxxhrFields: { withCredentials: true },fetch 是 credentials: 'include',且服务端要相应放宽声明——三者缺一即失败。其二,非简单请求(如带自定义头的 POST)浏览器会先发一次探询请求确认,见到"多一次请求"的日志不要惊慌,那是安检流程。

安全底线收拢

本章把散落的安全要点归成一页清单,逐条自查:

  • 响应当 HTML 填入前先想来源.loadhtml() 填入的内容里若含脚本或事件属性,注入就地生效。不可信来源一律先转义或只用 text()
  • JSONP 只连可信源,回调名永不拼用户输入;新项目一律 CORS。
  • 防重复提交:提交类请求在 beforeSend 里禁用按钮、complete 里恢复,弱网下的重复下单多半缺这一步。
  • 超时与错误兜底:每个请求都要有 timeout;全局 ajaxError 统一提示,避免"静默失败"变成"用户以为成功"。
  • 错误信息不外泄细节:面向用户的提示说"加载失败请重试"即可,技术细节进日志不进步骤弹窗。

通道选型:三张过境签证怎么挑

通道 前提 能力边界 适用判断
同源代理 开发期或网关层可配转发 无边界,浏览器眼里始终同源 本地联调外部接口、生产由网关统一收口
CORS 服务端愿意配响应头 全方法支持、可带凭据 自家多域名、可信合作方,新项目首选
JSONP 服务端支持回调包装 只 GET、错误无回调 存量接口已是 JSONP 且不可改动时

选型次序从上往下:能同源就同源,能 CORS 就 CORS,JSONP 只做存量兼容。开发期的代理转发值得多说一句——把外部地址映射成本地路径,既绕开了浏览器的安检报错,也让代码里的地址与环境无关,上线换网关配置即可,页面代码一个字不用改。

病例:控制台那行跨域红字的三种病因

背景:联调日,页面请求合作方接口,控制台甩出跨域阻止的红字。同屏症状不同,处理也不同,按三种病因分诊。

病因一,协议不一致:页面是本地开发服务器,接口是远端地址,协议域名端口全对不上。处理走代理转发,本地映射后红字消失。病因二,服务端声明没配到位:接口在浏览器网络面板里能看到响应体,但脚本读不到——这是"拦读不拦发"的典型现场,返回体就在那里给你看,就是不让脚本碰。处理找服务端同事配放行头,配好后同一段代码直接工作。病因三,带凭据却没声明:请求本身通过,但 Cookie 死活带不过去。处理是前端 xhrFields 与服务端凭据声明同时到位,缺一半都失败。

解读:跨域故障的分诊口诀是"先看三件套差在哪,再看响应体到没到,最后查凭据配没配"。三种病因在控制台的报错几乎同屏同款,区分它们靠的是网络面板的证据而不是红字本身——这也是 6.1 生命周期站点表的应用题。

要点复盘

  • JSONP 三个环节:备回调、脚本标签取数、执行即交付;本质是数据伪装成代码。
  • 三宗局限:只 GET、错误无回、执行来路代码;回调名与数据源的可信度是两条红线。
  • CORS 是现代正路:服务端声明放行,前端照常请求;带凭据与非简单请求各有额外配置。
  • 安全五查:HTML 来源、JSONP 红线、防重复提交、超时兜底、错误信息分级。
  • 至此体外通道全线贯通:原理、主力器械、快捷工具、侧路与安检,数据链路可以独立成军了。

七章只剩最后一站。器械用过一轮,该进保养车间了:工具函数、性能账本与时代迁移。


作者与出处
原作者: 灏天文库
来源:灏天文库
整理: 灏天文库整理
由灏天文库平台收录,内容或由平台用户上传,仅供学习交流
发布者: 作者: 灏天文库 转发
评论区 (0)
U