开始之前

Base64URL 是适用于 URL 和文件名的 Base64 变体:加号换成减号,斜杠换成下划线,填充等号通常省略。JWT 的 Header、Payload 和 Signature 三段都使用不带填充的 Base64URL,但解码出 Claims 并不等于验签成功。

边看边操作在 Base64 编解码 中打开实际工作区
01

用同一组字节对比字符表

UTF-8 文本“ÿ?”的标准 Base64 是 w78/,Base64URL 是 w78_。变化的只是最后一个字符表符号,解码后得到的字节完全相同。

示例
UTF-8 输入:ÿ?
标准 Base64:w78/
Base64URL:w78_
02

理解等号只是填充

Base64 每三个字节输出四个字符。结尾不足三个字节时,等号只负责补齐四字符分组:余一个字节补两个等号,余两个字节补一个,刚好整除则不需要。

示例
输入字节数 mod 3 = 0 → 不填充
输入字节数 mod 3 = 1 → ==
输入字节数 mod 3 = 2 → =
03

只在解码器需要时补位

JWT 分段可以先把减号和下划线换回标准字符,再补到四的倍数。如果长度除以四余一,说明内容本身不合法,不应该盲目追加等号。

示例
function decodeBase64Url(segment) {
  if (segment.length % 4 === 1) throw new Error("Invalid Base64URL length");
  const normalized = segment.replace(/-/g, "+").replace(/_/g, "/");
  const padded = normalized + "=".repeat((4 - normalized.length % 4) % 4);
  const bytes = Uint8Array.from(atob(padded), c => c.charCodeAt(0));
  return new TextDecoder("utf-8", { fatal: true }).decode(bytes);
}
04

Java 8 直接使用 URL 解码器

java.util.Base64 已经区分标准、URL 和 MIME 变体。JWT Header 与 Payload 应使用 getUrlDecoder,不要先交给标准解码器再靠字符串替换修补。

示例
import java.nio.charset.StandardCharsets;
import java.util.Base64;

String payload = "eyJzdWIiOiIxMjMiLCJleHAiOjE5MDAwMDAwMDB9";
byte[] decoded = Base64.getUrlDecoder().decode(payload);
System.out.println(new String(decoded, StandardCharsets.UTF_8));
// {"sub":"123","exp":1900000000}
05

解析之后仍要验签

解码只会展示任何人都能伪造的文本。信任 sub、role、exp、aud 等 Claims 前,服务端还必须使用可信密钥验签、固定允许算法,并检查 issuer、audience、时间和业务授权。

工具中的无填充 Base64URL

截图中格式已选择 Base64URL,并关闭等号填充。结果 w78_ 可以直接放进 URL 路径或参数,不会出现需要转义的斜杠和加号。

ParseNest 工具生成无等号填充 Base64URL w78_ 的实际截图
同一组 UTF-8 字节使用 URL 安全字符表编码,并省略等号填充。

怎样选择正确的 Base64 变体

对比项标准 Base64Base64URL / JWT
第 62、63 个字符+ 和 /- 和 _
等号填充通常保留JWT 通常省略
典型场景文件、邮件、API BodyJWT、OAuth、URL 参数
Java 8 APIBase64.getDecoder()Base64.getUrlDecoder()
要点总结
  • Base64URL 只替换字符表,不会改变解码后的字节。
  • JWT 分段按规范通常省略等号,不是数据丢失。
  • 遇到减号和下划线时应使用 Base64URL 解码器。
  • 读出 JWT Claims 不代表签名和授权已经验证。