0
点赞
收藏
分享

微信扫一扫

JSONP 原理详解

JSONP 原理详解

JSONP (JSON with Padding) 是一种绕过浏览器同源策略限制的跨域数据请求技术,它巧妙地利用了 HTML <script> 标签的跨域特性实现数据获取。

关键机制

  1. 利用 <script> 标签的跨域能力
  • 浏览器允许跨域加载 JS 文件
  • 不受同源策略限制
  1. 动态回调函数
  • 客户端定义全局回调函数
  • 服务器将数据"包裹"在该函数调用中返回
  1. 即时执行
  • 返回的 JS 代码到达客户端后立即执行
  • 自动触发预定义的回调函数

实现步骤详解

1. 客户端实现

<script>
// 步骤1:定义全局回调函数
function handleUserData(data) {
  console.log('收到用户数据:', data);
  document.getElementById('result').innerHTML = 
    `用户: ${data.name}, 年龄: ${data.age}`;
}

// 步骤2:动态创建 script 标签
function fetchData() {
  const script = document.createElement('script');
  
  // 设置带回调函数名的请求URL
  script.src = 'https://api.example.com/user?callback=handleUserData';
  
  // 步骤3:添加到DOM触发请求
  document.body.appendChild(script);
}
</script>

<button onclick="fetchData()">获取用户数据</button>
<div id="result"></div>

2. 服务器端响应

// Node.js 示例
const http = require('http');
const url = require('url');

const server = http.createServer((req, res) => {
  const { query } = url.parse(req.url, true);
  const callback = query.callback;
  
  if (req.url.startsWith('/user')) {
    // 模拟用户数据
    const userData = JSON.stringify({ 
      name: "张三", 
      age: 28,
      email: "zhangsan@example.com"
    });
    
    // 关键步骤:返回JS函数调用
    res.writeHead(200, {'Content-Type': 'application/javascript'});
    res.end(`${callback}(${userData})`);
  }
});

server.listen(3000);

服务器响应内容

// 实际返回给客户端的内容
handleUserData({
  "name": "张三",
  "age": 28,
  "email": "zhangsan@example.com"
})

JSONP 工作流程

  1. 客户端初始化
  • 定义全局回调函数(如 handleUserData
  • 创建 <script> 标签并设置 src 属性
  • URL 中包含回调函数名参数(?callback=handleUserData
  1. 发起请求
  • 浏览器发现 <script> 标签
  • 向目标服务器发起 GET 请求
  1. 服务器响应
  • 服务器解析 URL 获取回调函数名
  • 准备 JSON 数据
  • 将数据包裹在回调函数调用中返回
  1. 客户端处理
  • 浏览器下载并执行返回的 JS 代码
  • 自动调用预定义的回调函数
  • 在回调函数中处理数据

安全机制

主要安全措施

  1. 白名单验证(服务器端):

// 只允许特定的回调函数名
const allowedCallbacks = ['handleUserData', 'getProductInfo'];

if (!allowedCallbacks.includes(callback)) {
  return res.status(400).end('Invalid callback');
}

  1. 内容类型验证

// 强制设置为 JavaScript 类型
res.setHeader('Content-Type', 'application/javascript');

  1. 输入过滤

// 防止XSS
const sanitizedData = JSON.stringify(data)
  .replace(/</g, '\\u003c'); // 转义尖括号

安全风险

  1. XSS
  • 恶意服务器可能返回危险代码
  • 防护:只信任已知安全源
  1. CSRF 风险
  • 自动携带用户 Cookie
  • 防护:使用 CSRF token
  1. 数据更改
  • 中间人可能修改响应
  • 防护:使用 HTTPS

与现代技术的对比

特性

JSONP

CORS

WebSocket

协议

HTTP(S)

HTTP(S)

WS(S)

请求类型

GET 仅

所有方法

双向通信

数据格式

JSONP

任意

二进制/文本

安全性

较低



浏览器支持

所有浏览器

IE10+

所有现代浏览器

复杂度

简单

中等(需服务器配置)


实际应用场景

1. 跨域数据获取

// 获取天气数据
function getWeather(city) {
  const script = document.createElement('script');
  script.src = `https://weather-api.com/data?city=${city}&callback=showWeather`;
  document.body.appendChild(script);
}

function showWeather(data) {
  console.log(`温度: ${data.temp}℃, 湿度: ${data.humidity}%`);
}

2. 第三方登录

// 使用社交媒体登录
function authWithProvider(provider) {
  const popup = window.open(
    `https://${provider}.com/auth?callback=handleAuth`,
    '_blank'
  );
}

function handleAuth(userData) {
  localStorage.setItem('user', JSON.stringify(userData));
}

JSONP 的局限性

  1. 仅支持 GET 请求
  • 无法发送 POST/PUT/DELETE 请求
  • URL 长度限制(约 2000 字符)
  1. 错误处理困难

// 超时处理示例
function fetchWithTimeout() {
  const TIMEOUT = 5000;
  let timedOut = false;
  
  setTimeout(() => {
    if (!timedOut) {
      timedOut = true;
      console.error('请求超时');
      // 清理资源
    }
  }, TIMEOUT);
  
  // 正常JSONP请求...
}

  1. 安全性限制
  • 无法设置自定义请求头
  • 无法进行身份验证
  1. 回调函数管理
  • 多个并发请求需要不同回调

let counter = 0;

function jsonpRequest(url, callback) {
  const funcName = `jsonpCallback_${Date.now()}_${counter++}`;
  
  // 动态创建回调
  window[funcName] = (data) => {
    callback(data);
    delete window[funcName]; // 清理
    document.body.removeChild(script);
  };
  
  const script = document.createElement('script');
  script.src = `${url}?callback=${funcName}`;
  document.body.appendChild(script);
}

现代替代方案

1. CORS (推荐)

// 客户端
fetch('https://api.example.com/data', {
  method: 'GET',
  headers: {
    'Content-Type': 'application/json'
  }
})

// 服务端设置
Access-Control-Allow-Origin: https://yourdomain.com
Access-Control-Allow-Methods: GET, POST, PUT

2. 代理服务器

客户端 → 同源代理服务器 → 目标API

3. WebSockets

const socket = new WebSocket('wss://api.example.com');

socket.onmessage = (event) => {
  console.log('收到数据:', JSON.parse(event.data));
};

总结

JSONP 的核心价值在于:

  1. 绕过同源策略:利用 <script> 标签的跨域特性
  2. 简单易实现:无需复杂配置
  3. 广泛兼容性:支持所有浏览器

尽管在现代开发中已被 CORS 取代,但理解 JSONP 的机制对于:

  • 维护老系统
  • 理解浏览器安全模型
  • 处理特殊场景

仍然具有重要意义。在实际应用中,优先考虑更安全的 CORS 方案,JSONP 仅作为兼容老浏览器的备选方案。

举报

相关推荐

0 条评论