跳到主要内容

url安全的base64编码

· 阅读需 4 分钟
Jason Lee
The owner of this blog site

url base64 编码有特殊逻辑。

需求:url query 的值用 base64 加密。

背景知识:

  1. 浏览器会把任何非 ascii 字符转化为 utf-8 字符,包括中文、特殊符号等。转换后的形式为以百分号后跟两位十六进制为单位的序列。

  2. 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 中具有特殊含义或保留用途,可能会导致传输或解析上的问题。具体来说:

  1. +/ 字符 :在 URL 中有特殊含义,+ 通常代表空格,而 / 则是路径分隔符。使用这些字符时,可能会被误解或需要额外的转义。

  2. = 字符 :传统 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);