1. 问题描述
今天用springboot做了一个接口,返回一个js调用的数据,js使用以下的调用方式:
$.getScript('http://localhost:8081/a/wx/jssdkInfo?jsonp=setjssdk&link=https%3A%2F%2F3g.xxxx.com%2Fdemo%2F20231127%2Fdemo.html');
或者
var d=document.createElement('script');
d.setAttribute('type','text/javascript');
d.setAttribute('src','http://localhost:8081/a/wx/jssdkInfo?jsonp=setjssdk&link=https%3A%2F%2F3g.xxxx.com%2Fdemo%2F20231127%2Fdemo.html');
document.getElementsByTagName('head')[0].appendChild(d);
接口方法如下:
@ResponseBody
@RequestMapping(value = "/wx/jssdkInfo", method = RequestMethod.GET)
public String jssdkInfo(HttpServletRequest request, HttpServletResponse response) {
String callback = ServletUtils.getParameter("jsonp", "callback");
if (callback == null || callback.equals("")) {
callback = "callback";
}
String signJson = "";
String jsonpStr = "(function(){ var json=DATA; window['" + callback + "'](json); })();";
try {
String link = ServletUtils.getParameter("link", "");
if (StringUtils.isEmpty(link)) {
link = request.getHeader("Referer");
}
String ticket = extraService.getJsapiTicket(appId, appSecret);
WeChatSignVo weChatSignVo = extraService.sign(appId, ticket, link);
signJson = JSONObject.toJSONString(weChatSignVo);
} catch (Exception e) {
logger.error("获取jssdkInfo失败", e);
signJson = "";
}
jsonpStr = jsonpStr.replaceAll("DATA", signJson);
return jsonpStr;
}
浏览器直接调用接口返回结果如下:
(function(){ var json={"appId":"wx3f30847ss677ca","nonceStr":"3956df8f-dfea-4188-a8c1-eb86e46b5d12","signature":"53779a94c4be877d73ea157cad520f44a3b3119f","timestamp":"1701138083"}; window['setjssdk'](json); })();
但是当我用上面的js调用时,错误出现了:
没有数据返回,查看详细信息:
由于我这个功能主要是做微信分享的,所以我在微信开发者工具里面也试一下,结果如下:
2. 解决思路
2.1 是否是跨域造成的呢?
这里微信的开发者工具给了我误导,让我一直认为是跨域造成的。可是我本身springboot已经解决了跨域问题啊,不论我怎么测试,也没有发现服务存在跨域问题。况且浏览器也没有明确有错误说出现了跨域问题,所以果断放弃了这个方向的探讨;
2.2 X-Content-Type-Options: nosniff 的问题?
既然浏览器给出了这个头,那是不是这个的问题。随着进一步的探讨发现,设置”X-Content-Type-Options: nosniff”响应标头,对 script 和 styleSheet 在执行是通过MIME 类型来过滤掉不安全的文件。
X-Content-Type-Options: nosniff
如果响应中接收到 “nosniff” 指令,则浏览器不会加载“script”文件,除非 MIME 类型匹配以下值之一:
“application/ecmascript”
“application/javascript”
“application/x-javascript”
“text/ecmascript”
“text/javascript”
“text/jscript”
“text/x-javascript”
“text/vbs”
“text/vbscript”
那么显示是我的头信息有问题,并且从浏览器的返回结果看,我这个接口返回的Content-Type是:
Content-Type: text/plain;charset=UTF-8
那么显然不符合上面的规则。那么问题就定位出来了。
3. 解决问题
修改spirngboot的接口:
@RequestMapping(value = "/wx/jssdkInfo", method = RequestMethod.GET)
public void jssdkInfo(HttpServletRequest request, HttpServletResponse response) {
String callback = ServletUtils.getParameter("jsonp", "callback");
if (callback == null || callback.equals("")) {
callback = "callback";
}
String signJson = "";
String jsonpStr = "(function(){ var json=DATA; window['" + callback + "'](json); })();";
try {
String link = ServletUtils.getParameter("link", "");
if (StringUtils.isEmpty(link)) {
link = request.getHeader("Referer");
}
String ticket = extraService.getJsapiTicket(appId, appSecret);
WeChatSignVo weChatSignVo = extraService.sign(appId, ticket, link);
signJson = JSONObject.toJSONString(weChatSignVo);
} catch (Exception e) {
logger.error("获取jssdkInfo失败", e);
signJson = "";
}
jsonpStr = jsonpStr.replaceAll("DATA", signJson);
try {
response.reset();
response.addHeader("Access-Control-Allow-Origin", "*");
response.setStatus(200);
response.setContentType("text/javascript");
response.setCharacterEncoding("utf-8");
response.getWriter().print(jsonpStr);
} catch (IOException e) {
e.printStackTrace();
}
}
这里设置了响应头信息:
response.setContentType("text/javascript");
接口返回的数据和之前一样:
(function(){ var json={"appId":"wx3f30847ss677ca","nonceStr":"1f6f164f-b550-4603-98df-23e6d028ba3e","signature":"cfd7998edd0312607b56233c725aef33c1c05f61","timestamp":"1701139151"}; window['setjssdk'](json); })();
页面js调用:
我们看到有数据了。
头信息:
返回的Content-type:text/javascript;charset=utf-8
微信开发者工具返回也正常了