本文由作者朱臻授权网易云社区发布。
1问题案例
曾在开发过程中,我们遇到了BASE64编码乱码的问题,该问题的场景如下:
当web前端,将带有中文字符的字符串base64编码后,传到后端。当后端将数据取出再传给web前端时,发生了编码混乱的问题。例如,当前端向后端传入并保存为"赵俊雅和刘德华的爷爷”字符串后,当需要取出时,取出的字符串是“赵䲺fṤ9b&9occ桹⭹⭀”。这样的编码错误问题导致数据上的bug,会造成不良的用户体验。
2 问题排查
当我们仔细查看该两段字符串时可以发现除了首字符相同,其他字符全部面目全非。这至少可以一定程度说明,字符串在顺序编码或解码中途发生了错误。所以我们将两段字符串分别进行base64编码还原,并进行对比。
6LW15L+K6ZuF5ZKM5YiY5b635Y2O55qE54i354i3(赵俊雅和刘德华的爷爷)
6LW15L K6ZuF5ZKM5YiY5b635Y2O55qE54i354i3(赵䲺fṤ9b&9occ桹⭹⭀)
对比可以看出两者仅有一个+号的区别,后者只是将前者的+替换成空格,这导致除首字母外,后续字母全部发生混乱,因为base64是连续编码,原理将在后面解释。
那为什么会让+编程空格呢?这将从url的规范来解释,我们不难发现前后端交互时,后端会将url都会将+自动转化为空格。这基本上可以解释上述乱码的问题了。
3原理解释:
顺着上述介绍的顺序我们总结下上面提到的几个概念。
3.1BASE64:
它是一种用小规模编码集去表示较大规模编码的方法。本人的理解是:BASE64是指以64为基,存在64种编号的意思。也就是2的6次方=6bit。这64个编码(0~63)分别可以表示 [A~Z] 、[a-z]、[0~9]和[+ 、/]号 。共64种编号。 而与我们熟悉的字节编码对应的是 3*8bit=4*6bit=24bit=2的24次方。根据等式,可以将一切以字节为单位的编码方式(如UTF-8和GBK)转化成 BASE64编码,反之亦然。
也就是将原本3个字节的内容转变成4个BASE字符。这样,从字符角度来讲是变得更长了,扩展成的比例为133%。
3.2URL编码规范:
我们常说的url地址也就是地址栏输入的那串字符串,WWW标准中规定了url中只能包含[0~9,a-z,A-Z]和一些特殊字符$-_.+!*'(),。而我们常用的汉字以及阿拉伯文,拉丁文等都不包含在该范围内,所以必须经过一定规则的编码才能被正确的传输。虽然说浏览器一般都带有自动转码功能,这样使得用户常常感觉不到这一规则的存在。但是,不幸的是,各方对该编码的规则是不同的,这也造成乱码的隐患。Chrome使用utf8,javascript使用unicode,ie采用系统默认编码方式如GB2312。另外一件非常值得注意的事情,浏览器向后端发送URL的时候会自动将 空格替换成+号,这在我们使用google查询的get方法时会明显看到。 query中的中文变成 %XXX,空格变成+。当后端接收到请求后会将URL中的+自动替换成空格。这就是困扰我们多时的BASE64编码乱码给我们带来的一个麻烦。
3.3encodeURIComponent():
该js的方法是前端常用的一种URL处理方式,该种方式结合了过去escape()和encodeURI()两种方法的优势,前者已经很少被使用了,严格的说前者并不是一种专门解决URL编码的方法,只是用于将字符进行unicode编码,并且对于“@ * _ + - . /”等特殊字符并没有做任何处理,这使得其对URL乱码问题并没有很好的解决。而后者encodeURI()实现了全字段的编码,不管是特殊字符还是普通字符编码后都是以utf-8的形式。那主流的encodeURIComponent(),无疑是最佳解决方案,它不会对普通字符编码,只对一些特殊字符进行编码如;/?:@&=+$,# ,这对BASE64是至关重要的。
4可行的解决方案
根据以上特点,我们对当前问题有一些建议性的解决方案。
4.1 对于普通问题直接采用encodeURIComponent() 处理,而BASE64的工作交给后端处理。
4.2 如果有必要在前端做BASE64 可以首先进行BASE64处理,再采用encodeURIComponent() 处理。
4.3 另外,还可以直接使用BASE64的变种,将“+”和“/”两个特殊字符用 URL安全的“-”-和“_”字符处理,当然任何脑洞更大的字符替换也是可以的,现成的方式有safety base64等。
本文只是分享个人处理该问题的经验,如遇错误之处,请各位批评指正,也欢迎各位探讨交流。
更多网易技术、产品、运营经验分享请访问网易云社区。
相关文章:
【推荐】 网易云易盾牵手百视通 助力广电领域新媒体内容安全
【推荐】 数据分析怎么更直观?十分钟构建数据看板