JWT令牌原理

33 阅读4分钟

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

原理

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

image-20241201154905189.png

组成

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

在这里插入图片描述

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

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

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

image-20241201155527627.png

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

使用

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

在这里插入图片描述

实战

创建和验证Token一般是分开的,但是为了方便理解,写到一起了。代码位置为:github.com/shidawuhen/…

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
}

执行效果为:在这里插入图片描述

使用jwt.io验证,如果secret不对在这里插入图片描述

如果secret正确

在这里插入图片描述

最后

大家如果喜欢我的文章,可以关注我的公众号(程序员麻辣烫)

我的个人博客为:shidawuhen.github.io/

往期文章回顾:

  1. 设计模式

  2. 招聘

  3. 思考

  4. 存储

  5. 算法系列

  6. 读书笔记

  7. 小工具

  8. 架构

  9. 网络

  10. Go语言