url安全的base64编码
url base64 编码有特殊逻辑。
需求:url query 的值用 base64 加密。
背景知识:
-
浏览器会把任何非 ascii 字符转化为 utf-8 字符,包括中文、特殊符号等。转换后的形式为以百分号后跟两位十六进制为单位的序列。
-
base64 编码/解码:编码过程,输入是二进制数,输出是
A-Z, a-z, 0-9, +, /
的组合。
一开始,用 js 中的 btoa/atob 和 encodeURIComponent 就能将 base64 编码和解码 url param 参数的过程实现出来。如下:
function encodeBase64(input) {
return btoa(encodeURIComponent(input)); // 标准Base64编码
}
function decodeBase64(urlSafeBase64) {
return decodeURIComponent(atob(base64)); // 解码为原始字符串
}
这里有一个小细节。也许你会认为 url param 传到后端,后端拿到的是 utf-8 格式的 value。实际上,后端会自动将 utf-8 转回到原始字符。比如:
const { ask } = query;
console.log("ask", ask);
ask 是 query 中的一个参数。假如是中文“你好”,那么这里打印的就是“你好”。因为 btoa 只接受 ascii 字符串,所以,encodeURIComponent 是必须的。
但是,上线后很快就报错了。报错信息为:InvalidCharacterError: Failed to execute 'atob' on 'Window': The string to be decoded is not correctly encoded.
. 也就是解码时没有收到正确编码的字符串。原因是传统的 Base64 编码中使用的字符(例如 +
、/
和 =
)在 URL 中具有特殊含义或保留用途,可能会导致传输或解析上的问题。具体来说:
-
+
和/
字符 :在 URL 中有特殊含义,+
通常代表空格,而/
则是路径分隔符。使用这些字符时,可能会被误解或需要额外的转义。 -
=
字符 :传统 Base64 编码的结果会在末尾填充=
,用于对齐编码块的长度。这些=
在 URL 传递中通常不需要,而且在 URL 中也会增加不便。
URL 安全的 Base64 编码做法
URL 安全的 Base64 编码将传统 Base64 编码中的字符替换为 URL 兼容的字符:
-
把
+
替换为-
-
把
/
替换为_
-
去掉结尾的
=
,或者保持填充但进行编码处理。
举个例子,编码后的字符串有加号,那么经由浏览器传到后端,加号就会变成空格,不满足 base64 字符串的格式,所以 atob 函数就会报错了。正确做法是,编码的时候把这些特殊字符替换成别的,然后解码的时候先把这些特殊字符换回来再进行解码。
完整示例:
function toUrlSafeBase64(input) {
const base64 = btoa(unescape(encodeURIComponent(input))); // 标准Base64编码
return base64.replace(/\+/g, "-").replace(/\//g, "_").replace(/=+$/, ""); // 替换字符并去掉尾部的=
}
function fromUrlSafeBase64(urlSafeBase64) {
let base64 = urlSafeBase64.replace(/-/g, "+").replace(/_/g, "/"); // 还原为标准Base64
while (base64.length % 4) {
base64 += "="; // 补足Base64长度
}
return decodeURIComponent(escape(atob(base64))); // 解码为原始字符串
}
const input = "我想查询南京市2024年所有订单总量";
const encoded = toUrlSafeBase64(input);
console.log("Encoded (URL Safe Base64):", encoded);
const decoded = fromUrlSafeBase64(encoded);
console.log("Decoded:", decoded);