来自 奥门威尼斯网址 2019-09-16 12:36 的文章
当前位置: 威尼斯国际官方网站 > 奥门威尼斯网址 > 正文

记一次换行引发的血案,AJAX跨域调用相关知识

奥门威尼斯网址 ,哪里来的回车符

顺着上面两个问题,由于第一个问题的成本比较低,先看了下回车符哪里来的问题。我优先看了下当前地址,发现在当前地址的时候已经有问题了。而其他路径进入的这个页面都是正常的,只有这个特殊情况下有问题,遂反馈给服务端反查一下。最后服务端(PHP)那查到原来是因为他们读取文件时按行分割没有注意到方法里会带着换行的问题。

大概就是服务端那会有一个 token 文件,里面按行记录着一堆 ID,服务端会使用 file() 读取这个文件,然后将每个 ID 都 map 成一个地址下发下去。使用 file() 的好处是它在读取文件的时候能自动输出一个按行分割后的数组,这样就不需要额外操作。不过服务端同学没有注意,PHP 文档里也非常清楚的写明了:

Note:

Each line in the resulting array will include the line ending, unless FILE_IGNORE_NEW_LINES is used, so you still need to use rtrim() if you do not want the line ending present.

via: 

也就是说这种方法默认分割后的数组每个数据是包含最后的那个换行符的!想要去掉换行符需要添加 FILE_IGNORE_NEW_LINES 的标记参数。我自己也试了下发现果真如此!可以看到数组的前三个里面字符串的长度都是 4。

奥门威尼斯网址 1

最后服务端从来源上解决了这个问题。

response.setHeader(“Access-Control-Allow-Origin”, “);

后记

好啦,问题的来龙去脉前因后果总算是查清楚了。虽然这里面有各种坑,虽然服务端已经帮忙处理了,但是我明白最主要的问题还是最后的那个前段拼接的问题。所以我以血泪的历史告诉大家 URL 拼接一定要编码别搞什么骚操作啊!同时后面我 git blame 查了下写这个代码的同学,虽然离职了…但是我还是想…

奥门威尼斯网址 2

1 赞 收藏 评论

奥门威尼斯网址 3

 

为什么没被转义

虽然问题解决了,但是我的第二个疑问其实还没有被解决。本来应该被转义的字符为什么没有被转义?是道德的沦丧还是人性的泯灭zepto 出问题了还是我们的代码里有什么潜在的风险?

奥门威尼斯网址 4

我先去检查了一下 zepto 自己本身,发现它们的所有数据拼接都没有问题,使用了 $.param() 方法,而该方法内是使用了 escape() 对键值都做了编码的。zepto 出问题是不可能的了,那只能是我自己代码里的问题了。回到业务代码里查了一圈,最后发现,在某个阴暗的角落,居然窝藏了这么一段代码:

JavaScript

data.topicListApi = location.protocol + `//imnerd.org/detail?u=${uid}&sign=${sign}&n=10&tid=${tid}${onlineTypeParam}${tagParam}${rawUrlParam}${topUrlParam}`;

1
data.topicListApi = location.protocol + `//imnerd.org/detail?u=${uid}&sign=${sign}&n=10&tid=${tid}${onlineTypeParam}${tagParam}${rawUrlParam}${topUrlParam}`;

这一堆参数不经过任何编码就直接进行字符串拼接的操作…

奥门威尼斯网址 5

首先运行

我的回调呢?

我们在内部是使用了 zepto 这个基础库的。由于添加回调方法名并做替换肯定是 zepto 内部的行为,所以当我查到这的时候我都懵了。难道一年难遇一次国际知名库的 BUG 就这样被我水逆的碰到了?怀着一脸惊讶的表情我打开了 zepto 的文件继续查了下去。最终我发现还真是 zepto 里面 callback=? 没有替换成功。定义好 callback 方法之后 zepto 里面是这么去替换接口地址的参数的:

JavaScript

// window[callbackName] = function(){ responseData = arguments } script.src = options.url.replace(/?(.+)=?/, '?$1=' + callbackName) document.head.appendChild(script)

1
2
3
4
5
6
7
// https://github.com/madrobby/zepto/blob/master/src/ajax.js#L121-L126
window[callbackName] = function(){
  responseData = arguments
}
 
script.src = options.url.replace(/?(.+)=?/, '?$1=' + callbackName)
document.head.appendChild(script)

通过正则匹配到两个 ? 后并替换最后一个 ? 为回调方法名。最开始我一直没绕过来,我在想这特么是什么骚操作?能匹配到 callback=? 么??callback=? 是可以匹配到,万一这个东西在后面 &callback=? 不就挂了么?最后才反应过来它这么做是直接匹配了整个地址后的 query,忽略了参数名称直接匹配 ?。对于这种操作,我只能说:

奥门威尼斯网址 6

那么在当前的情况下这种操作会有什么问题呢,我怀着不服输的精神又查了下去。结果我发现在这种情况下这个正则居然跪了!最后我在这打印出来看,发现传进去的 URL 里面多了一个回车符,导致这段正则失效了。因为我们知道正则里面的 . 元字符是匹配除了回车符以外的所有字符。(这无语的BUG…果然水逆就算是啥也不干问题也会自动找上门啊…

奥门威尼斯网址 7

当我查到这的时候我就思考了两个问题:

  1. 这个回车从哪里来的?参数里怎么会有回车?因为这个参数是直接从当前页的 URL 获取的,所以是我们在拼接的时候操作的有问题,还是服务端下发的当前地址就有问题?
  2. 为什么到了最后 URL 地址这还有回车,正常情况下到这步的时候应该库都会对其进行转码编译了,例如回车符会被编成 %0A 这样其实 zepto 内部再处理就没有问题了呀?所以是哪里给漏编码了呢?

JSONP是以动态创建script标签为基础的一种编程技巧,来实现跨域获取JSON数据。

记一次换行引发的血案

2018/06/19 · 基础技术 · 换行

原文出处: 怡红公子   

话说最近真是流年不利,感觉各种BUG犹如天灾一样全部冒出来了,这不昨天又解了一个非常无语的问题,大概是关于换行和正则的臭虫,下面给大家吐槽一下。

index_jsonp.html中,我们在$.ajax的参数上有点变化:

数据“野”了

昨天同事反馈某个页面的数据没有正常显示,最开始我还以为是接口没有返回数据,结果看了下请求发现接口有正常的数据呀。没办法就一路反查回去,最后查到居然代码里接口请求抛错了?!因为定义了 Promise 的 catch 流程,所以也没有把错误抛出来。因为之前这个页面都是正常的,很久都没有改动所以我第一反应是这个数据异常了,查了半天的数据格式问题。可是问题就在于明明看到数据是正常的呀,服务端没有报错,接口数据也是可以正常解析的。最后我突然想起来,我们的接口是 JSONP 的会不会是 JSONP 功能挂了?查了一下果然是这样。

奥门威尼斯网址 8

我们都知道 JSONP 需要定义一个 callback 回调名称,最后数据加载的时候执行这个回调返回数据才算成功。可以看到图里面虽然加了 callback=? 但是并没有对 ? 进行替换,服务端那边应该也是增加了校验这种情况下并没有给我们添加回调方法名。实际上服务端这里的处理都是没问题的,因为即使 ? 被添加到数据里了也会有问题,因为 ? 是逻辑运算符并不能被定义成变量。而没有加回调方法的后果就是虽然接口返回正常,但是最终数据没有人接收就“野”了。

这里要注意,如果只是通过AJAX向另一个服务器发送请求而不要求数据返回,是不受跨域限制的。浏览器只是限制不能访问另一个域的数据,即不能访问返回的数据,并不限制发送请求。

支持目前所有浏览器,只是在实现方式上需要前后端代码有一点约定配合。

但是,要注意由于JSONP是以script标签的src属性加载的,因此参数会收到URL长度的限制,只能适用于传入参数内容不多的场景。

如果不限定跨域访问的地址,可以把域名部分设置为*:

JSONP并不是一个官方协议,其本质上是一种巧妙的跨域获取JSON数据的编程技巧。

type改成了get,JSONP只支持get请求,这个参数在JSONP场景下其实是可以忽略的,即使改成post,也会依然按get模式;
dataType改成了jsonp,这个参数标明要采用JSONP方式进行调用;
jsonp: “x5callback”,这个参数其实是一个约定的参数名,用于后端按照这个参数名获取一个回调函数名;
jsonpCallback:这个参数用来指定上面那个参数对应的回调函数名,如果不指定,jQuery会自动生成一个随机的函数名。
add_jsonp.jsp中,我们在最后数据返回部分做了一点处理:

test-client/index.html

接下来我们F12启动浏览器开发者工具,点击按钮后就会进入JS调试。

启动Tomcat后,在浏览器中,分别测试

CORS方案实现简单,同时支持GET和POST请求,但是不支持IE9及以下浏览器。这时看官要问了,这么多浏览器不支持,这技术怎么用啊?手机啊!目前市面上所有的手机浏览器是全部支持CORS的,如果是为手机提供跨域服务CORS就够了。

下载资源:ajax-cors-jsonp.zip

JSONP方案实现需要前后端配合,支持GET请求,支持所有浏览器,只是传入的参数内容受限于URL长度限制。

接下来我们先解析代码:

 

小结:

index_cors.html与index.html的差异仅是ajax调用的地址从add.jsp换成了add_cors.jsp,我们再来看add.jsp和add_cors.jsp的区别,会发现只增加了一行代码:

IE9及以下浏览器在安全设置里控制是否允许跨域数据访问:

AJAX跨域调用相关知识-CORS和JSONP

一个可以正常启动的Tomcat,默认端口8080;
下载示例代码包ajax-cors-jsonp.zip,解压到Tomcat的webapps下,示例代码包中有test-client和test-service两个文件夹,分别包含我们示例的客户端和服务端代码;
模拟一个多域环境,修改“C:WindowsSystem32driversetchosts”(如果文件不能编辑保存,需要在文件属性中去掉只读),在文件内容后面追加:
127.0.0.1       www.aaa.com
127.0.0.1       www.bbb.com

加载后,按照JS的特性,这些代码会立即执行。而jQuery在这个之前已经动态创建了一个以随机函数名为名称的全局函数,用于接收返回数据,再往后jQuery通过一系列的逻辑代码最终把返回值给到了我们的success回调函数中。

我们首先来看实现,JSONP在实现上要比CORS稍微麻烦一点点,前后端要有点配合。

 

debugger;jQuery18203749695811420679_1439276096319({“sum”: 25})

但是我们项目开发过程中,经常会遇到在一个页面的JS代码中,需要通过AJAX去访问另一个服务器并返回数据,这时候就会受到浏览器跨域的安全限制了。

本文由威尼斯国际官方网站发布于奥门威尼斯网址,转载请注明出处:记一次换行引发的血案,AJAX跨域调用相关知识

关键词: