日本免费高清视频-国产福利视频导航-黄色在线播放国产-天天操天天操天天操天天操|www.shdianci.com

學無先后,達者為師

網站首頁 編程語言 正文

Go語言如何使用golang-jwt/jwt/v4進行JWT鑒權詳解_Golang

作者:楓花海 ? 更新時間: 2022-11-09 編程語言

前言

最近寫的項目中用到了JWT鑒權,因此做個記錄

原先的jwt-go倉庫已經不再維護,遷移到了github.com/golang-jwt/jwt/v4

但是網上大多數還是v3版本的使用教程,建議使用更加安全的v4

1.什么是JWT

JSON Web Token (JWT)是一個開放標準(RFC 7519),它定義了一種緊湊的、自包含的方式,用于作為JSON對象在各方之間安全地傳輸信息。特別適用于分布式站點的單點登錄(SSO)場景。JWT的聲明一般被用來在身份提供者和服務提供者間傳遞被認證的用戶身份信息,以便于從資源服務器獲取資源,也可以增加一些額外的其它業務邏輯所必須的聲明信息,該token也可直接被用于認證,也可被加密。

2.JWT的數據結構

實際的JWT由三部分組成,如下圖

中間用點(.)分隔成三個部分。注意,JWT 內部是沒有換行的,這里只是為了便于展示,將它寫成了幾行。JWT的三個部分依次如下:

  • Header(頭部)
  • Payload(負載)
  • Signature(簽名)

寫成一行就是Header.Payload.Signature

2.1 Header

Header 部分是一個 JSON 對象,描述 JWT 的元數據,通常是下面的樣子

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

上面代碼中,alg屬性表示簽名的算法(algorithm),默認是HMAC SHA256(寫成 HS256);typ屬性表示這個令牌(token)的類型(type),JWT 令牌統一寫為JWT

將上面的 JSON 對象使用 Base64URL 算法(詳見后文)轉成字符串就成了第一部分Header

2.2 Payload

Payload 部分也是一個 JSON 對象,用來存放實際需要傳遞的數據。JWT 規定了7個官方字段,供選用。

  • iss (issuer):簽發人
  • exp (expiration time):過期時間
  • sub (subject):主題
  • aud (audience):受眾
  • nbf (Not Before):生效時間
  • iat (Issued At):簽發時間
  • jti (JWT ID):編號

我們還可以在這個部分自己定義字段,下面就是一個例子

{
  "sub": "1234567890",
  "name": "John Doe",
  "admin": true
}

注意,JWT 默認是不加密的,任何人都可以讀到,所以不要把秘密信息放在這個部分。

這個 JSON 對象也要使用 Base64URL 算法轉成字符串。

2.3 Signature

Signature 部分是對前兩部分的簽名,防止數據篡改。

首先,需要指定一個密鑰(secret)。這個密鑰只有服務器才知道,不能泄露給用戶。然后,使用 Header 里面指定的簽名算法(默認是 HMAC SHA256),按照下面的公式產生簽名。

HMACSHA256(
  base64UrlEncode(header) + "." +
  base64UrlEncode(payload),
  secret)

2.4 Base64URL

前面提到,HeaderPayload 串型化的算法是 Base64URL。這個算法跟 Base64 算法基本類似,但有一些小的不同。

JWT 作為一個令牌(token),有些場合可能會放到 URL(比如 api.example.com/?token=xxx)。Base64 有三個字符+,/和=,在 URL 里面有特殊含義,所以要被替換掉:=被省略、+替換成-,/替換成_ 。這就是 Base64URL 算法。

算出簽名以后,把 HeaderPayloadSignature 三個部分拼成一個字符串,每個部分之間用"點"(.)分隔,就可以返回給用戶。

3使用JWT

安裝

go install "github.com/golang-jwt/jwt/v4"

生成Token

定義claimsserect

type MyClaims struct {
	Phone string `json:"phone"`
	jwt.RegisteredClaims  // 注意!這是jwt-go的v4版本新增的,原先是jwt.StandardClaims
}

var MySecret = []byte("手寫的從前") // 定義secret,后面會用到

生成token

// 這里傳入的是手機號,因為我項目登陸用的是手機號和密碼
func MakeToken(phone string) (tokenString string, err error) {
	claim := MyClaims{
		Phone: phone,
		RegisteredClaims: jwt.RegisteredClaims{
			ExpiresAt: jwt.NewNumericDate(time.Now().Add(3 * time.Hour * time.Duration(1))), // 過期時間3小時
			IssuedAt:  jwt.NewNumericDate(time.Now()),     // 簽發時間
			NotBefore: jwt.NewNumericDate(time.Now()),     // 生效時間
		}}
	token := jwt.NewWithClaims(jwt.SigningMethodHS256, claim) // 使用HS256算法
	tokenString, err = token.SignedString(MySecret)
	return tokenString, err
}

解析token

func Secret() jwt.Keyfunc {
	return func(token *jwt.Token) (interface{}, error) {
		return []byte("手寫的從前"), nil  // 這是我的secret
	}
}

func ParseToken(tokenss string) (*MyClaims, error) {
	token, err := jwt.ParseWithClaims(tokenss, &MyClaims{}, Secret())
	if err != nil {
		if ve, ok := err.(*jwt.ValidationError); ok {
			if ve.Errors&jwt.ValidationErrorMalformed != 0 {
				return nil, errors.New("that's not even a token")
			} else if ve.Errors&jwt.ValidationErrorExpired != 0 {
				return nil, errors.New("token is expired")
			} else if ve.Errors&jwt.ValidationErrorNotValidYet != 0 {
				return nil, errors.New("token not active yet")
			} else {
				return nil, errors.New("couldn't handle this token")
			}
		}
	}
	if claims, ok := token.Claims.(*MyClaims); ok && token.Valid {
		return claims, nil
	}
	return nil, errors.New("couldn't handle this token")
}

參考:

JSON Web Token 入門教程

總結

原文鏈接:https://blog.csdn.net/weixin_44294408/article/details/122095919

欄目分類
最近更新