JSONP原理与实现:解决跨域请求的脚本注入方案

作者:极速慢跑

时间:2026-10-28

浏览:74

本文详细解析JSONP(JSON with Padding)技术,旨在解决浏览器同源策略限制下的跨域数据获取问题。通过利用script标签不受同源策略限制的特性,将数据封装在回调函数中返回,实现了跨域通信。文章展示了从原生JavaScript动态创建script标签到jQuery封装实现的完整过程,并指出了潜在的安全风险。

文章目录

JSONP原理与跨域问题

JSONP全称JSON with Padding,是解决跨域请求资源的一种方案。通常客户端通过Ajax与Web服务交互获取服务器数据,但若目标数据位于不同域,浏览器的同源策略会出于安全考虑阻止直接请求,导致无法跨域获取资源。例如在localhost域下的books.php尝试向test.com域的books.php发起GET请求以获取书籍列表,即构成跨域请求。

代码如下:

$.ajax({
type:'get',
url:'http://test.com/books.php'
        });

执行上述代码会报错:XMLHttpRequest cannot load http://test.com/books.php. Origin http://localhost is not allowed by Access-Control-Allow-Origin。JSONP正是为解决此问题而诞生。

JSONP实现机制

尽管存在同源策略限制,但HTML中并非所有资源都受此约束。为优化加载速度或节省流量,页面常引用Google或微软的jQuery CDN。此时可通过以下方式引入jQuery:

代码如下:

<script type="text/javascript"
src="http://ajax.googleapis.com/ajax/libs/jquery/1.7.1/jquery.min.js">
</script>

iframe、img、style、script等元素的src属性可直接向不同域请求资源,JSONP正是利用script标签的这一特性来实现跨域数据获取。

假设localhost下的books.php需要获取test.com域的书籍列表,而test.com中书籍数据存储在books.xml文件中。

代码如下:

<?xml version="1.0"?>
<books>
<book name="JavaScript: The Defiitive Guide" publisher="O'Reilly Media, Inc.">
<author>David Flanagan</author>
</book>
<book name="PHP anf MySQL Web Development" publisher="Perason Education">
<author>Luke Welling</author>
<author>Laura Thomson</author>
</book>
<book name="HTTP: The Defiitive Guide" publisher="O'Reilly Media, Inc.">
<author>David Courley</author>
<author>Brian Totty</author>
</book>
</books>

JavaScript无法直接解析books.xml,因此test.com需提供机制将XML转换为JSON格式(这也是JSONP名称的由来,尽管返回数据不一定是JSON,但JSON便于处理),并动态拼接JavaScript调用语句返回。本例直接使用PHP页面进行拼接。


test.com/bookservice.php


代码如下:

<?php
$path=$_SERVER["DOCUMENT_ROOT"].'/books.xml';
$json=json_encode(simplexml_load_file($path));
$callbackFn=$_GET['callback'];
 echo "$callbackFn($json);"
?>

上述代码首先将XML文件内容转换为JSON对象。


代码如下:

{"book":[
{"@attributes":{"name":"JavaScript: The Defiitive Guide","publisher":"O'Reilly Media, Inc."},"author":"David Flanagan"},
{"@attributes":{"name":"PHP anf MySQL Web Development","publisher":"Perason Education"},"author":["Luke Welling","Laura Thomson"]},
{"@attributes":{"name":"HTTP: The Defiitive Guide","publisher":"O'Reilly Media, Inc."},"author":["David Courley","Brian Totty"]}
]}

随后将JSON数据拼接为一条JavaScript语句返回给localhost处理。由于test.com不知道具体的回调函数名,localhost需在请求URL中传入名为callback(名称可自定义,但需两端一致)的参数来指定回调函数。以下是localhost发送请求的方式:


原生JavaScript实现

localhost/books.php


代码如下:

<!DOCTYPE html>
<html>
<head>
<title>Books</title>
<?php include('/components/headerinclude.php');?></head>
<style type="text/css">
.book-title
        {
font-size: 15px;
font-weight:bold;
margin-top:6px;
        }
.book-info
 {
 color:#ccc;
 font-style:italic;
 border-bottom:dashed 1px #ccc;
 }
 </style>
</head>
<body>
 <div style="margin:20px;">
 <div style="font-size:16px;font-weight:bold;">Books</div>
 <div id="books">
 </div>
 </div>
</body>
</html>

目标是在id为books的div中展示所有书籍。首先定义一个用于处理数据的回调函数,结合前述JSON格式,代码如下:


代码如下:

function displayBooks(books){
var books=books.book;
var booksContainer=document.getElementById('books');
for(var i=0;i<books.length;i++){
var tmp=Array();
tmp.push('<div class="book-title">'+books[i]['@attributes'].name+'</div>');
tmp.push('<div class="book-info">');
tmp.push('<div>Publisher: '+books[i]['@attributes'].publisher+'</div>');
tmp.push('<div>Author(s): ');
if(typeof books[i].author=='string'){
tmp.push(books[i].author);
}else{
var authors=books[i].author;
for(var j=0;j<authors.length;j++){
tmp.push(authors[j]+'&emsp;');
                    }
                }
tmp.push('</div>'); //end of author
tmp.push('</div>'); //end of book info
booksContainer.innerHTML+=tmp.join('');
            }
        }

接下来是关键步骤:实现JSONP请求方法。


代码如下:

function getBooks(){
var script=document.createElement('script');
script.setAttribute('type','text/javascript');
script.setAttribute('src','http://test.com/bookservice.php?callback=displayBooks');
document.body.appendChild(script);
        }
getBooks();

在getBooks()方法中动态创建script标签,设置其src属性指向test.com提供的数据服务接口,并传入回调函数。通过Chrome控制台可观察到该请求。


JSONP原理与跨域问题

通过上述方式,即可在localhost环境下获取test.com的书籍数据。
JSONP原理与跨域问题

jQuery实现

jQuery封装实现

jQuery对JSONP进行了封装,将其集成在Ajax中。虽然两者机制不同,但jQuery提供了更便捷的调用方式。jQuery版本实现如下:


代码如下:

function getBooks(){
$.ajax({
type:'get',
url:'http://test.com/bookservice.php',
dataType:'jsonp',
jsonp:'callback',
jsonpCallback:'displayBooks'
            });
        }

看起来与普通Ajax无异,但简化了操作,无需手动创建script标签。只需指定dataType为jsonp,并通过两个参数分别指定回调函数名。

安全性注意事项

使用JSONP存在一定安全风险。若请求的站点不可信,返回的方法调用中可能包含恶意代码。因此应仅向信任的站点发送请求。此外,XSS攻击常利用JSONP向站点注入恶意代码。

实用工具推荐

电子包浆

绿化、压缩、噪点和虚拟水印,自由调节做旧程度。

使用 ›
图片压缩

选择画质和输出格式,预览压缩前后的大小。

使用 ›
相关文章