JWT介绍及go代码示例

192 阅读3分钟

JWT是什么

jwt是Json Web Token简写,是一种基于json的开放标准RFC7519。其信息主要存放在客户端,主要用于在不同的服务终端之间安全地传输信息,常用于Web应用程序的身份验证和鉴权,如SSO登录

特点

设计紧凑且安全,分为三段构成,分别为header,payload,signature, 三部分均为Base64URL编码后的数据,通过点号连接。其中header和payload为json object base64URL编码后得到的字符串, signature是对header和payload的加密,用于验证jwt的合法性。jwt结构图如下:

image.png

组成

  • header

    header只有两个字段,分别是:

    • alg: 指定的加密算法,生成signature使用,如sha256, hmac等。
    • typ: 说明jwt的类型,一般都是JWT

    如下:

    { 
       "alg": "HS256",
       "typ": "JWT" 
    }
    
  • payload

    也被称为claim,是用来传递信息的地方,标准只有一些建议字段,并无强制要求必传的字段。用户可根据需求自行设计。如设置一个用户名和过期时间

    { 
        "name": "test", 
        "expire": 1726234962 
    }
    

    注: payload采用base64url编码,是可以被解码的,故在设计payload时不要传递敏感信息。

  • signature

    签名是主要目的是为了保证jwt的完整性,其生成步骤主要是:

    • 将header和payload进行base64url编码,然后使用点拼接,组成字符串 s1
    • 使用header中指定的算法和后端存放的secret对 s1进行加密,得到加密传 s2
    • 对最后的s2进行base64URL编码即可

    比如使用使用HS256进行加密:

    s1 = Base64UrlEncode(Header) + "." + Base64UrlEncode(payload) 
    signature = Base64UrlEncode(HS256(s1, secret))
    

    如何校验jwt合法性:服务端获取到jwt串中的header和payload信息后,根据加密规则生成签名串并与jwt中的signature比对即可。

go代码演示

使用golang-jwt

  • 下载 go get -u github.com/golang-jwt/jwt/v5

  • 引用 import "github.com/golang-jwt/jwt/v5"

  • 完整代码

    
    package main
    import (
       "crypto"
       "crypto/hmac"
       "encoding/base64"
       "encoding/json"
       "fmt"
       "github.com/golang-jwt/jwt/v5"
    )
    
    func main() {
     secret := []byte("1223333")
      token, _ := createToken(secret)
      fmt.Println(token)
      validToken(token, secret)
    }
    
    // 加密
    func createToken(secret []byte) (string, error) {
    
      token := jwt.NewWithClaims(jwt.SigningMethodHS256, jwt.MapClaims{
    	"name": "john",
    	"role": "admin",
      })
      tokenString, err := token.SignedString(secret)
      return tokenString, err
     }
    
    // 解密
    func validToken(tokenString string, secret []byte) {
    
    token, err := jwt.Parse(tokenString, func(token *jwt.Token) (interface{}, error) {
    	return secret, nil
    })
    
     if err != nil {
    	fmt.Println("err", err)
    	return
     }
    
     fmt.Printf("valid:%v , payload:%v\n", token.Valid, token.Claims)
    } 
    

    执行结果:

image.png

原生加密实现

func main() {

  header := map[string]interface{}{
  	"typ": "JWT",
  	"alg": "HS256",
  }
  h, _ := json.Marshal(header)

  hs := base64.RawURLEncoding.EncodeToString(h)
  fmt.Println("header==>", hs)

  claims := map[string]interface{}{
  	"name": "john",
  	"role": "admin",
  }

  c, _ := json.Marshal(claims)
  cs := base64.RawURLEncoding.EncodeToString(c)
  fmt.Println("payload==>", cs)

  signingString := hs + "." + cs
  hmacSampleSecret := []byte("1223333")

  hasher := hmac.New(crypto.SHA256.New, hmacSampleSecret)
  hasher.Write([]byte(signingString))

  sign := hasher.Sum(nil)
  ss := base64.RawURLEncoding.EncodeToString(sign)
  fmt.Println("sign==>", ss)

}

输出结果

image.png

原生实现按照jwt定义进行实现,可以看出整个算法的实现过程比较简单。这也是jwt的好处之一,即实现简单。

总结

最后总结下jwt的优缺点以及适用场景。jwt适用sso登录,甚至应用之间的认证也可以使用jwt处理,总体而言jwt简单且灵活。

  • 优点

    • 易于使用: 传输方式无限制,header,url等均可。对比cookie不会存在跨域问题。
    • 无状态:服务端不需要维护token信息,架构设计上较为简洁。
    • 灵活:对payload无强制限制,可自由设置传输信息。
  • 缺点

    • 无法撤销,一般只要发出,有效期之前都是有效的