记录一个 Go base64 解码的问题
这里记录一个使用 Go base64 标准库解码 token 时遇到的问题。
import (
"encoding/base64"
"fmt"
)
func tokenParser(token string) {
tokenStr, err := base64.StdEncoding.DecodeString(token)
if err != nil {
fmt.Println(err)
}
}
上面的代码输出了错误 illegal base64 data at input byte xxx
。
上面的错误是因为 jwt 的 base64 是 no padding 的,要使用 base64.RawStdEncoding
来解码:
tokenStr, err := base64.RawStdEncoding.DecodeString(token)
if err != nil {
fmt.Println(err)
}
什么是 no padding
要知道什么是 no padding,需要先简单了解一下 base64 的原理。
base64 编码原理
base64 是网络上最常见的用于传输 8 bit 字节码的编码方式之一,base64 就是一种基于 64 个可打印字符来表示二进制数据的方法。
64 个打印字符:
索引 | 对应字符 | 索引 | 对应字符 | 索引 | 对应字符 | 索引 | 对应字符 |
---|---|---|---|---|---|---|---|
0 | A | 17 | R | 34 | i | 51 | z |
1 | B | 18 | S | 35 | j | 52 | 0 |
2 | C | 19 | T | 36 | k | 53 | 1 |
3 | D | 20 | U | 37 | l | 54 | 2 |
4 | E | 21 | V | 38 | m | 55 | 3 |
5 | F | 22 | W | 39 | n | 56 | 4 |
6 | G | 23 | X | 40 | o | 57 | 5 |
7 | H | 24 | Y | 41 | p | 58 | 6 |
8 | I | 25 | Z | 42 | q | 59 | 7 |
9 | J | 26 | a | 43 | r | 60 | 8 |
10 | K | 27 | b | 44 | s | 61 | 9 |
11 | L | 28 | c | 45 | t | 62 | + |
12 | M | 29 | d | 46 | u | 63 | / |
13 | N | 30 | e | 47 | v | ||
14 | O | 31 | f | 48 | w | ||
15 | P | 32 | g | 49 | x | ||
16 | Q | 33 | h | 50 | y |
base64 就是使用上面的 64 个可打印字符来表示二进制数据。2^6 = 64
也就是说,上面 64 个字符的索引,最多用 6 个 bit 就可以表示了。
但是常用的字符集没有使用 6 bit 表示的,比如 ASCII 码需要 8 个 bit 来表示。
那么如何使用 6 个 bit 表示 8 个 bit 的数据?
使用 4*6
个 bit 来存储 3*8
个 bit。例如:
Son
经过 base64 编码以后转换成了 U29u
。
3 个 ASCII 字符刚好转换成对应的 4 个 base64 字符,但是如果需要转换的字符不是 3 的倍数,也就是说在分组时最后一组不够 3 个字节如何转换?
base64 有一条规则:当需要转换的字符不是 3 的倍数时,一律采用补 0 的方式凑足 3 的倍数。例如:
S
经过 base64 编码以后转换成了 Uw==
。第二组末尾补 4 个 0 转换后为字符 w
。剩下两个字节使用 =
填补。
no padding 是非填补的意思,也就是说当需要转换的字符不是 3 的倍数时,剩下的 1 到 2 个 0 字节不使用 =
填补。
Go base64 标准库
Go base64 标准库的源码:
// StdEncoding is the standard base64 encoding, as defined in
// RFC 4648.
var StdEncoding = NewEncoding(encodeStd)
// URLEncoding is the alternate base64 encoding defined in RFC 4648.
// It is typically used in URLs and file names.
var URLEncoding = NewEncoding(encodeURL)
// RawStdEncoding is the standard raw, unpadded base64 encoding,
// as defined in RFC 4648 section 3.2.
// This is the same as StdEncoding but omits padding characters.
var RawStdEncoding = StdEncoding.WithPadding(NoPadding)
// RawURLEncoding is the unpadded alternate base64 encoding defined in RFC 4648.
// It is typically used in URLs and file names.
// This is the same as URLEncoding but omits padding characters.
var RawURLEncoding = URLEncoding.WithPadding(NoPadding)
StdEncoding 代表的是标准加解密,URLEncoding,则是 URL 加解密。RawStdEncoding 和 RawURLEncoding 非别对应它们在 no padding 时应该 使用的方法。