基本结构

Header(头部)

Hedaer 部分用于描述该 JWT 的基本信息,比如其类型(通常是 JWT)以及所使用的签名算法(如 HMAC SHA256 或 RSA)。

Payload(负载)

Payload 部分包含所传递的声明。声明是关于实体(通常是用户)和其他数据的语句。声明可以分为三种类型:注册声明、公共声明 和 私有声明。

注册声明

这些声明是预定义的,非必须使用的但被推荐使用。官方标准定义的注册声明有 7 个

Claim(声明)含义
iss(Issuer)发行者,标识 JWT 的发行者。
sub(Subject)主题,标识 JWT 的主题,通常指用户的唯一标识
aud(Audience)观众,标识 JWT 的接收者
exp(Expiration Time)过期时间。标识 JWT 的过期时间,这个时间必须是将来的
nbf(Not Before)不可用时间。在此时间之前,JWT 不应被接受处理
iat(Issued At)发行时间,标识 JWT 的发行时间
jti(JWT ID)JWT 的唯一标识符,用于防止 JWT 被重放(即重复使用)

公共声明:可以由使用 JWT 的人自定义,但为了避免冲突,任何新定义的声明都应已在 IANA JSON Web Token Registry 中注册或者是一个公共名称,其中包含了碰撞防抗性名称(Collision-Resistant Name)。

私有声明:发行和使用 JWT 的双方共同商定的声明,区别于 注册声明 和 公共声明。

Signature(签名)

为了防止数据篡改,将头部和负载的信息进行一定算法处理,加上一个密钥,最后生成签名。如果使用的是 HMAC SHA256 算法,那么签名就是将编码后的头部、编码后的负载拼接起来,通过密钥进行 HMAC SHA256 运算后的结果。

基本使用

安装

1
go get -u github.com/golang-jwt/jwt/v5

生成Token对象

NewWithClaims 函数

1
2
3
4
5
6
7
 mapClaims := jwt.MapClaims{
"iss": "发行者",
"sub": "主题",
"aud": "观众",
}
token := jwt.NewWithClaims(jwt.SigningMethodHS256, mapClaims)
fmt.Println(token != nil) // true

New 函数

1
2
  token := jwt.New(jwt.SigningMethodHS256)
fmt.Println(token != nil) // true

生成JWT字符串

使用SignedString()函数进行生成

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
func GenerateJwt(key any, method jwt.SigningMethod, claims jwt.Claims) (string, error) {
token := jwt.NewWithClaims(method, claims)//生成Token
return token.SignedString(key)//生成JWT字符串
}

func main() {
jwtKey := make([]byte, 32) // 生成32字节(256位)的密钥
if _, err := rand.Read(jwtKey); err != nil {
panic(err)
}
jwtStr, err := GenerateJwt(jwtKey, jwt.SigningMethodHS256, jwt.MapClaims{
"iss": "发行者",
"sub": "主题",
"aud": "观众",
})
if err != nil {
panic(err)
}
fmt.Println(jwtStr)

解析JWT字符串

Parse 函数

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
func ParseJwt(key any, jwtStr string, options ...jwt.ParserOption) (jwt.Claims, error) {
token, err := jwt.Parse(jwtStr, func (token *jwt.Token) (interface{}, error) {//解析 JWT 时,使用指定的密钥进行验证。
return key, nil
}, options...)
if err != nil {
return nil, err
}
// 校验 Claims 对象是否有效,基于 exp(过期时间),nbf(不早于),iat(签发时间)等进行判断(如果有这些声明的话)。
if !token.Valid {
return nil, errors.New("invalid token")
}
return token.Claims, nil
}

func main() {
jwtKey := make([]byte, 32) // 生成32字节(256位)的密钥
if _, err := rand.Read(jwtKey); err != nil {
panic(err)
}

token := jwt.NewWithClaims(jwt.SigningMethodHS256, jwt.MapClaims{
"iss": "发行者",
"sub": "主题",
"aud": "观众",
"exp": time.Now().Add(time.Second * 10).UnixMilli(),
})
jwtStr, err := token.SignedString(jwtKey)
if err != nil {
panic(err)
}

// 解析 jwt
claims, err := ParseJwt(jwtKey, jwtStr, jwt.WithExpirationRequired())
if err != nil {
panic(err)
}
fmt.Println(claims)

ParseWithClaims 函数

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
func ParseJwtWithClaims(key any, jwtStr string, options ...jwt.ParserOption) (jwt.Claims, error) {
mc := jwt.MapClaims{}
token, err := jwt.ParseWithClaims(jwtStr, mc, func (token *jwt.Token) (interface{}, error) {
return key, nil
}, options...)
if err != nil {
return nil, err
}
// 校验 Claims 对象是否有效,基于 exp(过期时间),nbf(不早于),iat(签发时间)等进行判断(如果有这些声明的话)。
if !token.Valid {
return nil, errors.New("invalid token")
}
return token.Claims, nil
}

func main() {
jwtKey := make([]byte, 32) // 生成32字节(256位)的密钥
if _, err := rand.Read(jwtKey); err != nil {
panic(err)
}

token := jwt.NewWithClaims(jwt.SigningMethodHS256, jwt.MapClaims{
"iss": "发行者",
"sub": "主题",
"aud": "观众",
})
jwtStr, err := token.SignedString(jwtKey)
if err != nil {
panic(err)
}

// 解析 jwt
claims, err := ParseJwtWithClaims(jwtKey, jwtStr)
if err != nil {
panic(err)
}
fmt.Println(claims)