Skip to content

Node系列 · Express:跨域之 JSONP

JSONP 是 CORS 之前的"老式"跨域方案——利用 <script> 标签不受同源策略限制的特性。本章讲清楚 JSONP 的工作原理、Express 实现、以及它为什么被 CORS 取代。

一、同源策略与跨域

1.1 同源

text
页面源:http://a.com/page.html
接口源:http://b.com/api/users

判定标准:协议 + 域名 + 端口 都相同才算同源。

例子是否同源
http://a.com/xhttp://a.com/y
http://a.com/xhttps://a.com/x❌(协议不同)
http://a.com/xhttp://b.com/x❌(域名不同)
http://a.com/xhttp://a.com:8080/x❌(端口不同)

1.2 浏览器不允许非同源请求

浏览器主动拦截 XMLHttpRequest / fetch 跨域响应——但 <img> / <script> / <link> 等标签不受同源限制

JSONP 利用的就是 <script> 的"特权"。

二、JSONP 原理

  1. 客户端定义全局函数 window.fn
  2. 动态创建 <script src="http://b.com/api?callback=fn">
  3. 服务端把数据包在 fn 调用里返回:fn([...])
  4. 浏览器把响应当 JS 执行,调用 fn 函数
  5. 客户端拿到数据

2.1 客户端代码

html
<script>
  function handleUsers(users) {
    console.log('拿到数据:', users);
  }
</script>

<!-- 动态加载 script -->
<script src="http://b.com/api/users?callback=handleUsers"></script>

2.2 服务端代码(Express)

javascript
app.get('/api/users', (req, res) => {
  const users = [{ id: 1, name: 'Alice' }, { id: 2, name: 'Bob' }];

  const callback = req.query.callback;
  if (callback) {
    // JSONP 模式:把数据包在函数调用里
    res.type('application/javascript');
    res.send(`${callback}(${JSON.stringify(users)})`);
  } else {
    // 普通 JSON 模式
    res.json(users);
  }
});

响应内容:

javascript
handleUsers([{"id":1,"name":"Alice"},{"id":2,"name":"Bob"}])

浏览器执行这段 JS → 调用 handleUsers → 客户端拿到数据。

三、JSONP 的致命缺陷

3.1 只能 GET

<script> 标签天然只支持 GET 请求——POST / PUT / DELETE 没法做。

3.2 难调试

服务端返回的是 JS 代码而非 JSON——浏览器 Network 面板看不到结构化响应,只能看到一段 JS 字符串。

3.3 安全风险

javascript
// 服务端可以执行客户端传来的 callback 函数名
const callback = req.query.callback;
res.send(`${callback}(${data})`);

// 如果 callback = "alert(1);evil()"
// 服务端返回:alert(1);evil({...})
// 浏览器执行:alert(1) + 调用 evil 函数

DANGER

JSONP 存在反射型 XSS 风险——callback 参数如果不过滤就被攻击者利用:

javascript
// ✅ 正确:限制 callback 只能是合法标识符
const callback = req.query.callback;
if (!/^[a-zA-Z_$][\w$]*$/.test(callback)) {
  return res.status(400).json({ error: 'invalid callback' });
}
res.send(`${callback}(${JSON.stringify(data)})`);

必须严格校验 callback——只允许字母数字下划线。

3.4 错误处理难

JSONP 失败只能靠超时——没有 HTTP 状态码,没有错误信息。

四、被 CORS 取代

CORS(Cross-Origin Resource Sharing)是 W3C 推荐的现代方案:

维度JSONPCORS
HTTP 方法仅 GET任意
错误处理HTTP 状态码标准
安全callback 注入风险受控 Origin 白名单
调试标准 HTTP 响应
浏览器支持所有浏览器所有现代浏览器

TIP

新项目不要用 JSONP。CORS 是更现代、更安全的方案,详见 cors 中间件 章节。

五、历史场景

JSONP 只在以下场景还有价值:

  • 调用 IE8/IE9 的老接口(不支持 CORS)
  • 调用只暴露 JSONP 的第三方接口(无法让对方改)

否则一律用 CORS

六、最佳实践

场景推荐
新项目跨域CORS(详见 跨域 CORScors 中间件
维护老 JSONP 接口必须严格过滤 callback 参数
客户端调用优先用 fetch / axios + CORS,不要再造 JSONP 轮子
服务端 JSONPcallback 强校验(只允许合法标识符)

七、小结

  • JSONP 利用 <script> 标签不受同源策略限制的特性
  • 客户端定义 callback 函数 → 服务端把数据包在 callback 调用里返回
  • 致命缺陷:仅 GET / 难调试 / callback 注入风险 / 错误处理难
  • 新项目用 CORS,不要用 JSONP
  • 维护 JSONP 接口时严格过滤 callback(只允许合法标识符),防 XSS