• 主页
  • 架构
  • 编程语言
  • 数据存储
  • 网络
  • VMware
  • 服务器
  • 组网
  • AI
  • 算法系列
  • 设计模式
  • 读书笔记
  • 思考
  • 工具
  • 其它技术

  • 主页
  • 架构
  • 编程语言
  • 数据存储
  • 网络
  • VMware
  • 服务器
  • 组网
  • AI
  • 算法系列
  • 设计模式
  • 读书笔记
  • 思考
  • 工具
  • 其它技术

JWT令牌原理

2024-12-01

以前写过浅谈OAuth2.0和常用密码算法介绍,这次学习了一下JWT。

原理

令牌分为两类,透明令牌是指根据Token获取不到什么有效信息,必须和授权服务器交互,OAuth2就是透明令牌。还有一种就是自包含令牌,Token里包含了核心信息,怎么知道Token是否伪造的呢?因为Token上同时包含了签名,只要检查方有签名所需要的密钥,就能辨别真伪。

image-20241201154905189

组成

JWT由三部分组成,Header+Claims+Signature,这三部分一般做base64url,通过.合并在一起,如下图所示。

image-20241201155151320

payload中有几个字段,意思为:

  • iss:颁发人
  • iat:颁发时间
  • exp:过期时间
  • aud:颁发给谁的
  • sub:主体
  • role:登录用户的信息

通过verify signature字段,能够看出签名是怎么计算出来的。要检验,必须有签名的“secret”。这么看是不是和支票有点相似。

image-20241201155527627

JWT可以通过https://jwt.io/进行解析验证。

使用

一般客户应用从授权服务器获取JWT后,可以直接请求对应的资源服务器,这些资源服务器会查看JWT的信息,并做签名验证,为什么可以做签名验证?因为他们有授权服务器签名的“secret”。

image-20241201155809321

实战

创建和验证Token一般是分开的,但是为了方便理解,写到一起了。代码位置为:https://github.com/shidawuhen/asap/blob/master/controller/oauth/jwt.go

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
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
package oauth

import (
"errors"
"fmt"
"github.com/gin-gonic/gin"
"github.com/golang-jwt/jwt/v5"
"net/http"
"time"
)

func JWT(c *gin.Context) {
mySigningKey := []byte("AllYourBase")

tokenString := creatJWT(mySigningKey)
checkJWT(tokenString, mySigningKey)
c.String(http.StatusOK, "ok")
}

func checkJWT(tokenString string, mySigningKey []byte) {
token, err := jwt.Parse(tokenString, func(token *jwt.Token) (interface{}, error) {
return mySigningKey, nil
})
switch {
case token.Valid:
fmt.Printf("正确的token %+v", token)
case errors.Is(err, jwt.ErrTokenMalformed):
fmt.Println("That's not even a token")
case errors.Is(err, jwt.ErrTokenSignatureInvalid):
// Invalid signature
fmt.Println("Invalid signature")
case errors.Is(err, jwt.ErrTokenExpired) || errors.Is(err, jwt.ErrTokenNotValidYet):
// Token is either expired or not active yet
fmt.Println("Timing is everything")
default:
fmt.Println("Couldn't handle this token:", err)
}
}

func creatJWT(mySigningKey []byte) string {
type MyCustomClaims struct {
Foo string `json:"foo"`
jwt.RegisteredClaims
}

// Create claims with multiple fields populated
claims := MyCustomClaims{
"hello",
jwt.RegisteredClaims{
// A usual scenario is to set the expiration time relative to the current time
ExpiresAt: jwt.NewNumericDate(time.Now().Add(24 * time.Hour)),
IssuedAt: jwt.NewNumericDate(time.Now()),
NotBefore: jwt.NewNumericDate(time.Now()),
Issuer: "asap",
Subject: "com",
ID: "1",
Audience: []string{"pzq"},
},
}

token := jwt.NewWithClaims(jwt.SigningMethodHS256, claims)
ss, err := token.SignedString(mySigningKey)
fmt.Println(ss, err)
return ss
}

执行效果为:image-20241201174802617

使用jwt.io验证,如果secret不对image-20241201174847925

如果secret正确

image-20241201174912204

扫一扫,分享到微信

微信分享二维码
团队分工
团建有感
© 2025 John Doe
Hexo Theme Yilia by Litten