GV

GeezerVison

The World Wide Web, restored to 800 pixels of glory.

Home|About|Gaming cabinet|Archives|How it works|Starter sites

On this site

  • Sign in
  • ActionsCI/CD and automation
  • CodespacesCloud development environments
  • All Features
  • Documentation
  • Training
  • Certifications
  • Build with Copilot
  • Customize Copilot
  • Integrations
  • Trending repos
  • GitHub Sponsors
  • Developer articles
  • Enterprises

This view

  • Original page
  • New address

GeezerVison is showing github.com — JSONP

JSONP

Jump to bottom
JoyChou edited this page Sep 24, 2019 · 8 revisions

自动添加为JSONP

代码

代码很简单:

@ControllerAdvice
public class JSONPAdvice extends AbstractJsonpResponseBodyAdvice {

    public JSONPAdvice() {
        super("callback", "cback"); // callback的参数名,可以为多个
    }
}

当有接口返回了Object(比如JSONObject或者JavaBean,但是不支持String),只要在参数中加入callback=test或cback=test就会自动变成JSONP接口。比如下面代码:

@RequestMapping(value = "/advice", produces = MediaType.APPLICATION_JSON_VALUE)
public JSONObject advice() {
    String info = "{\"name\": \"JoyChou\", \"phone\": \"18200001111\"}";
    return JSON.parseObject(info);
}

虽然上面代码指定了response的content-type为application/json,但是在AbstractJsonpResponseBodyAdvice类中会设置为application/javascript,提供给前端调用。

设置content-type为application/javascript的代码:

protected MediaType getContentType(MediaType contentType, ServerHttpRequest request, ServerHttpResponse response) {
	return new MediaType("application", "javascript");
}

并且还会判断callback的参数只是否是有效的,代码如下:

private static final Pattern CALLBACK_PARAM_PATTERN = Pattern.compile("[0-9A-Za-z_\\.]*");

protected boolean isValidJsonpQueryParam(String value) {
	return CALLBACK_PARAM_PATTERN.matcher(value).matches();
}

返回:

/**/test({"phone":"18200001111","name":"JoyChou"});

安全风险

使用AbstractJsonpResponseBodyAdvice能避免callback导致的XSS问题,但是会带来一个新的风险:可能有的JSON接口强行被设置为了JSONP,导致JSON劫持。所以使用AbstractJsonpResponseBodyAdvice,需要默认校验所有jsonp接口的referer是否合法。

注意

在Spring Framework 5.1,移除了AbstractJsonpResponseBodyAdvice类。Springboot 2.1.0 RELEASE默认使用spring framework版本5.1.2版本。也就是在SpringBoot 2.1.0 RELEASE及以后版本都不能使用该功能,用CORS替代。

https://docs.spring.io/spring/docs/5.0.x/javadoc-api/org/springframework/web/servlet/mvc/method/annotation/AbstractJsonpResponseBodyAdvice.html

Will be removed as of Spring Framework 5.1, use CORS instead.

前端调用代码

  • 使用ajax的jsonp调用方式,运行后会弹框JoyChou。
  • 使用script src方式,运行后会弹框JoyChou。

使用ajax的jsonp调用方式代码:

<html>
<head>
<meta charset="UTF-8" />
<script src="https://cdn.bootcss.com/jquery/3.4.1/jquery.min.js"></script>
</head>
<body>
<script language="JavaScript">
$(document).ready(function() {
	$.ajax({
		url:'http://localhost:8080/jsonp/advice',
		dataType:'jsonp',
		success:function(data){
			alert(data.name)
		}
	});
});
</script>
</body>
</html>

script src方式代码:

<html>

<script>
	function test(data){
		alert(data.name);
	}
</script>
<script src=http://localhost:8080/jsonp/referer?callback=test></script>
</html>

空Referer绕过

有时候开发同学为了测试方便,JSONP接口能直接访问,不直接访问做了Referer限制。正常来讲,前端发起的请求默认都会带着Referer,所以简单说下如何绕过空Referer。

Poc 1

<html>
<meta name="referrer" content="no-referrer" />

<script>
	function test(data){
		alert(data.name);
	}
</script>
<script src=http://localhost:8080/jsonp/emptyReferer?callback=test></script>
</html>

Poc2

<iframe src="javascript:'<script>function test(data){alert(data.name);}</script><script src=http://localhost:8080/jsonp/emptyReferer?callback=test></script>'"></iframe>

Reference

  • https://stackoverflow.com/questions/6817595/remove-http-referer
  • https://drops.org.cn/PENETRATION/Json-with-Padding.html

Clone this wiki locally

You are visitor 01402503 — GeezerVison does not store the pages you look up.
Best viewed with Netscape Navigator 4.7 or Internet Explorer 6 at 800×600. No cookies required.