本文由做者朱臻受權網易雲社區發佈。javascript
1問題案例前端
曾在開發過程當中,咱們遇到了BASE64編碼亂碼的問題,該問題的場景以下:java
當web前端,將帶有中文字符的字符串base64編碼後,傳到後端。當後端將數據取出再傳給web前端時,發生了編碼混亂的問題。例如,當前端向後端傳入並保存爲"趙俊雅和劉德華的爺爺」字符串後,當須要取出時,取出的字符串是「趙䲺fṤ9b&9occ桹⭹⭀」。這樣的編碼錯誤問題致使數據上的bug,會形成不良的用戶體驗。web
2 問題排查編程
當咱們仔細查看該兩段字符串時能夠發現除了首字符相同,其餘字符所有面目全非。這至少能夠必定程度說明,字符串在順序編碼或解碼中途發生了錯誤。因此咱們將兩段字符串分別進行base64編碼還原,並進行對比。後端
6LW15L+K6ZuF5ZKM5YiY5b635Y2O55qE54i354i3(趙俊雅和劉德華的爺爺)瀏覽器
6LW15L K6ZuF5ZKM5YiY5b635Y2O55qE54i354i3(趙䲺fṤ9b&9occ桹⭹⭀)安全
對比能夠看出二者僅有一個+號的區別,後者只是將前者的+替換成空格,這致使除首字母外,後續字母所有發生混亂,由於base64是連續編碼,原理將在後面解釋。google
那爲何會讓+編程空格呢?這將從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等。
更多網易技術、產品、運營經驗分享請訪問網易雲社區。
相關文章:
【推薦】 手滑把庫給刪了,跑路前應該作的事。。。
【推薦】 如何安全地運行用戶的 JavaScript 腳本