GO語言Gin包(JWT使用)


轉載自 https://blog.csdn.net/codeSquare/article/details/98980234

GO語言Gin包(JWT使用)

JWT 的全稱叫做 JSON WEB TOKEN,在目前前后端系統中使用較多。

JWT 是由三段構成的。分別是 HEADER,PAYLOAD,VERIFY SIGNATURE,它們生成的信息通過 . 分割。

https://blog.wangjunfeng.com/post/golang-jwt/#3-簽名-signature

  • header 是由 一個 typ 和 alg 組成,typ 會指明為 JWT,而 alg 是所使用的加密算法。
{
  "alg": "HS256",
  "typ": "JWT"
}

PAYLOAD

  • payload 是 JWT 的載體,也就是要承載的信息。這段信息是可以自定義的,可以定義要存放什么信息,那些字段。

  • 該部分信息不宜過多,它會影響 JWT 生成的大小,還有就是請勿將敏感數據存入該部分,該端數據前端是可以解析獲取 token 內信息的。
    <\br>

  • 官方給了七個默認字段,我們可以不全部使用,也可以加入我們需要的字段。

iss: Token簽發者。格式是區分大小寫的字符串或者uri,用於唯一標識簽發token的一方。
sub: Token的主體,即它的所有人。格式是區分大小寫的字符串或者uri。
aud: 接收Token的一方。格式為區分大小寫的字符串或uri,或者這兩種的數組。
exp: Token的過期時間,格式為時間戳。
nbf: 指定Token在nbf時間之前不能使用,即token開始生效的時間,格式為時間戳。
iat: Token的簽發時間,格式為時間戳。
jti: 指此Token的唯一標識符字符串。主要用於實現唯一性保證,防止重放。

VERIFY SIGNATURE

  • 該部分是由算法計算完成的。
  • 對 header 進行 base64Url 編碼,對 payload 進行 base64Url 編碼,兩端完成編碼后通過 . 進行連接起來。
base64UrlEncode(header).base64UrlEncode(payload)
  • 完成上述步驟后,就要通過 header 里指定的加密算法對上部分進行加密,同時還要插入一個密鑰,來確保 JWT 簽發是安全的。

三部分都完成后,通過使用 . 將三部分分割,生成了上圖所示的 JWT 。

JWT 登錄原理

  • 簡單的說就是當用戶登錄的時候,服務器校驗登錄名稱和密碼是否正確,正確的話,會生成 JWT 返回給客戶端。客戶端獲取到 JWT 后要進行保存,之后的每次請求都會講 JWT 攜帶在頭部,每次服務器都會獲取頭部的 JWT 是否正確,如果正確則正確執行該請求,否者驗證失敗,重新登錄。

Gin 生成 JWT

go 語言的 JWT 庫 "github.com/dgrijalva/jwt-go"

  • 登錄方法進行改造。
// 省略代碼
expiresTime := time.Now().Unix() + int64(OneDayOfHours)
//OneDayOfHours用來設置過期時間,這里設置的時一天
//可以使用int  60*60*24

claims := jwt.StandardClaims{
    Audience:  user.Username,     // 受眾
    ExpiresAt: expiresTime,       // 失效時間
    Id:        string(user.ID),   // 編號
    IssuedAt:  time.Now().Unix(), // 簽發時間
    Issuer:    "gin hello",       // 簽發人
    NotBefore: time.Now().Unix(), // 生效時間
    Subject:   "login",           // 主題
}
//通過 StandardClaims 生成標准的載體,也就是上文提到的七個字段,其中 編號設定為 用戶 id。

var jwtSecret = []byte("gin Hello")
//jwtSecret 是我們設定的密鑰

tokenClaims := jwt.NewWithClaims(jwt.SigningMethodHS256, claims)
//通過 HS256 算法生成 tokenClaims ,這就是我們的 HEADER 部分和 PAYLOAD。

token, err := tokenClaims.SignedString(jwtSecret)
//生成token 

token =  "Bearer "+ token
//將 token 和 Bearer 拼接在一起,同時中間用空格隔開。
//生成 Bearer Token 
完整代碼
func CreateJwt(ctx *gin.Context) {
	// 獲取用戶
	user := &model.User{}
      /*
      type User struct {
	gorm.Model
	Username string `json:"username"`
	Password string `json:"password"`
	Roles    []Role `json:"role" gorm:"many2many:roles"`
      }
      */
	result := &model.Result{
		Code:    200,
		Message: "登錄成功",
		Data:    nil,
	}
      /*
      type Result struct {
	Code    int         `json:"code" example:"000"`
	Message string      `json:"message" example:"請求信息"`
	Data    interface{} `json:"data" `
      }
      */
	if e := ctx.BindJSON(&user); e != nil {
		result.Message = "數據綁定失敗"
		result.Code = http.StatusUnauthorized
		ctx.JSON(http.StatusUnauthorized, gin.H{
			"result": result,
		})
	}
	u := user.QueryByUsername()
	if u.Password == user.Password {
		expiresTime := time.Now().Unix() + int64(config.OneDayOfHours)
		claims := jwt.StandardClaims{
			Audience:  user.Username,     // 受眾
			ExpiresAt: expiresTime,       // 失效時間
			Id:        string(user.ID),   // 編號
			IssuedAt:  time.Now().Unix(), // 簽發時間
			Issuer:    "gin hello",       // 簽發人
			NotBefore: time.Now().Unix(), // 生效時間
			Subject:   "login",           // 主題
		}
		var jwtSecret = []byte(config.Secret)
		tokenClaims := jwt.NewWithClaims(jwt.SigningMethodHS256, claims)
		if token, err := tokenClaims.SignedString(jwtSecret); err == nil {
			result.Message = "登錄成功"
			result.Data = "Bearer " + token
			result.Code = http.StatusOK
			ctx.JSON(result.Code, gin.H{
				"result": result,
			})
		} else {
			result.Message = "登錄失敗"
			result.Code = http.StatusOK
			ctx.JSON(result.Code, gin.H{
				"result": result,
			})
		}
	} else {
		result.Message = "登錄失敗"
		result.Code = http.StatusOK
		ctx.JSON(result.Code, gin.H{
			"result": result,
		})
	}
}

Gin 校驗 Token

  • 解析 token 方法,parseToken()
  • eg:
func parseToken(token string) (*jwt.StandardClaims, error) {
	jwtToken, err := jwt.ParseWithClaims(token, &jwt.StandardClaims{}, func(token *jwt.Token) (i interface{}, e error) {
		return []byte(config.Secret), nil
	})
	if err == nil && jwtToken != nil {
		if claim, ok := jwtToken.Claims.(*jwt.StandardClaims); ok && jwtToken.Valid {
			return claim, nil
		}
	}
	return nil, err
}
  • 通過傳入我們的 token , 來對 token 進行解析

  • 完整的中間件代碼
func Auth() gin.HandlerFunc {
	return func(context *gin.Context) {
		result := model.Result{
			Code:    http.StatusUnauthorized,
			Message: "無法認證,重新登錄",
			Data:    nil,
		}
		auth := context.Request.Header.Get("Authorization")
                //
		if len(auth) == 0 {
			context.Abort()
			context.JSON(http.StatusUnauthorized, gin.H{
				"result": result,
			})
		}
		auth = strings.Fields(auth)[1]
		// 校驗token
		_, err := parseToken(auth)
		if err != nil {
			context.Abort()
			result.Message = "token 過期" + err.Error()
			context.JSON(http.StatusUnauthorized, gin.H{
				"result": result,
			})
		} else {
			println("token 正確")
		}
		context.Next()
	}
}

訪問

GET http://localhost:8080
Content-Type: application/json
Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJhdWQiOiIxMjMiLCJleHAiOjE1NjQ3OTQzNjIsImp0aSI6Ilx1MDAwMCIsImlhdCI6MTU2NDc5NDM0MiwiaXNzIjoiZ2luIGhlbGxvIiwibmJmIjoxNTY0Nzk0MzQyLCJzdWIiOiJsb2dpbiJ9.uQxGMsftyVFtYIGwQVm1QB2djw-uMfDbw81E5LMjliU


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM