Go JWT

本文將使用這個庫來實現(xiàn)我們生成JWT和解析JWT的功能撇簿。

使用

go get github.com/golang-jwt/jwt/v4

默認Claim

如果我們直接使用JWT中默認的字段,沒有其他定制化的需求則可以直接使用這個包中的和方法快速生成和解析token。


// 用于簽名的字符串
var mySigningKey = []byte("liwenzhou.com")

// GenRegisteredClaims 使用默認聲明創(chuàng)建jwt
func GenRegisteredClaims() (string, error) {
    // 創(chuàng)建 Claims
    claims := &jwt.RegisteredClaims{
        ExpiresAt: jwt.NewNumericDate(time.Now().Add(time.Hour * 24)), // 過期時間
        Issuer:    "qimi",                                             // 簽發(fā)人
    }
    // 生成token對象
    token := jwt.NewWithClaims(jwt.SigningMethodHS256, claims)
    // 生成簽名字符串
    return token.SignedString(mySigningKey)
}

// ParseRegisteredClaims 解析jwt
func ValidateRegisteredClaims(tokenString string) bool {
    // 解析token
    token, err := jwt.Parse(tokenString, func(token *jwt.Token) (interface{}, error) {
        return mySigningKey, nil
    })
    if err != nil { // 解析token失敗
        return false
    }
    return token.Valid
}

自定義Claims

我們需要定制自己的需求來決定JWT中保存哪些數(shù)據(jù),比如我們規(guī)定在JWT中要存儲username信息袄秩,那么我們就定義一個MyClaims結構體如下:

// CustomClaims 自定義聲明類型 并內(nèi)嵌jwt.RegisteredClaims
// jwt包自帶的jwt.RegisteredClaims只包含了官方字段
// 假設我們這里需要額外記錄一個username字段,所以要自定義結構體
// 如果想要保存更多信息轻绞,都可以添加到這個結構體中
type CustomClaims struct {
    // 可根據(jù)需要自行添加字段
    Username             string `json:"username"`
    jwt.RegisteredClaims        // 內(nèi)嵌標準的聲明
}

然后我們定義JWT的過期時間搏嗡,這里以24小時為例:

constTokenExpireDuration=time.Hour*24

接下來還需要定義一個用于簽名的字符串:

// CustomSecret 用于加鹽的字符串
varCustomSecret=[]byte("夏天夏天悄悄過去")

生成JWT

我們可以根據(jù)自己的業(yè)務需要封裝一個生成 token 的函數(shù)窿春。

// GenToken 生成JWT
func GenToken(username string) (string, error) {
    // 創(chuàng)建一個我們自己的聲明
    claims := CustomClaims{
        username, // 自定義字段
        jwt.RegisteredClaims{
            ExpiresAt: jwt.NewNumericDate(time.Now().Add(TokenExpireDuration)),
            Issuer:    "my-project", // 簽發(fā)人
        },
    }
    // 使用指定的簽名方法創(chuàng)建簽名對象
    token := jwt.NewWithClaims(jwt.SigningMethodHS256, claims)
    // 使用指定的secret簽名并獲得完整的編碼后的字符串token
    return token.SignedString(CustomSecret)
}

解析JWT

根據(jù)給定的 JWT 字符串,解析出數(shù)據(jù)。

// ParseToken 解析JWT
func ParseToken(tokenString string) (*CustomClaims, error) {
    // 解析token
    // 如果是自定義Claim結構體則需要使用 ParseWithClaims 方法
    token, err := jwt.ParseWithClaims(tokenString, &CustomClaims{}, func(token *jwt.Token) (i interface{}, err error) {
        // 直接使用標準的Claim則可以直接使用Parse方法
        //token, err := jwt.Parse(tokenString, func(token *jwt.Token) (i interface{}, err error) {
        return CustomSecret, nil
    })
    if err != nil {
        return nil, err
    }
    // 對token對象中的Claim進行類型斷言
    if claims, ok := token.Claims.(*CustomClaims); ok && token.Valid { // 校驗token
        return claims, nil
    }
    return nil, errors.New("invalid token")
}

在gin框架中使用JWT

首先我們注冊一條路由/auth谁尸,對外提供獲取Token的渠道:

 r.POST("/auth",authHandler)

我們的authHandler定義如下:

func authHandler(c *gin.Context) {
    // 用戶發(fā)送用戶名和密碼過來
    var user UserInfo
    err := c.ShouldBind(&user)
    if err != nil {
        c.JSON(http.StatusOK, gin.H{
            "code": 2001,
            "msg":  "無效的參數(shù)",
        })
        return
    }
    // 校驗用戶名和密碼是否正確
    if user.Username == "q1mi" && user.Password == "q1mi123" {
        // 生成Token
        tokenString, _ := GenToken(user.Username)
        c.JSON(http.StatusOK, gin.H{
            "code": 2000,
            "msg":  "success",
            "data": gin.H{"token": tokenString},
        })
        return
    }
    c.JSON(http.StatusOK, gin.H{
        "code": 2002,
        "msg":  "鑒權失敗",
    })
    return
}

用戶通過上面的接口獲取Token之后,后續(xù)就會攜帶著Token再來請求我們的其他接口纽甘,這個時候就需要對這些請求的Token進行校驗操作了良蛮,很顯然我們應該實現(xiàn)一個檢驗Token的中間件,具體實現(xiàn)如下:

// JWTAuthMiddleware 基于JWT的認證中間件
func JWTAuthMiddleware() func(c *gin.Context) {
    return func(c *gin.Context) {
        // 客戶端攜帶Token有三種方式 1.放在請求頭 2.放在請求體 3.放在URI
        // 這里假設Token放在Header的Authorization中悍赢,并使用Bearer開頭
        // 這里的具體實現(xiàn)方式要依據(jù)你的實際業(yè)務情況決定
        authHeader := c.Request.Header.Get("Authorization")
        if authHeader == "" {
            c.JSON(http.StatusOK, gin.H{
                "code": 2003,
                "msg":  "請求頭中auth為空",
            })
            c.Abort()
            return
        }
        // 按空格分割
        parts := strings.SplitN(authHeader, " ", 2)
        if !(len(parts) == 2 && parts[0] == "Bearer") {
            c.JSON(http.StatusOK, gin.H{
                "code": 2004,
                "msg":  "請求頭中auth格式有誤",
            })
            c.Abort()
            return
        }
        // parts[1]是獲取到的tokenString决瞳,我們使用之前定義好的解析JWT的函數(shù)來解析它
        mc, err := ParseToken(parts[1])
        if err != nil {
            c.JSON(http.StatusOK, gin.H{
                "code": 2005,
                "msg":  "無效的Token",
            })
            c.Abort()
            return
        }
        // 將當前請求的username信息保存到請求的上下文c上
        c.Set("username", mc.Username)
        c.Next() // 后續(xù)的處理函數(shù)可以用過c.Get("username")來獲取當前請求的用戶信息
    }
}

注冊一個/home路由,發(fā)個請求驗證一下吧左权。

r.GET("/home", JWTAuthMiddleware(), homeHandler)

func homeHandler(c *gin.Context) {
    username := c.MustGet("username").(string)
    c.JSON(http.StatusOK, gin.H{
        "code": 2000,
        "msg":  "success",
        "data": gin.H{"username": username},
    })
}
?著作權歸作者所有,轉載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末皮胡,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子赏迟,更是在濱河造成了極大的恐慌屡贺,老刑警劉巖,帶你破解...
    沈念sama閱讀 216,591評論 6 501
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件锌杀,死亡現(xiàn)場離奇詭異甩栈,居然都是意外死亡,警方通過查閱死者的電腦和手機糕再,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,448評論 3 392
  • 文/潘曉璐 我一進店門量没,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人突想,你說我怎么就攤上這事殴蹄。” “怎么了猾担?”我有些...
    開封第一講書人閱讀 162,823評論 0 353
  • 文/不壞的土叔 我叫張陵袭灯,是天一觀的道長。 經(jīng)常有香客問我绑嘹,道長妓蛮,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,204評論 1 292
  • 正文 為了忘掉前任圾叼,我火速辦了婚禮蛤克,結果婚禮上,老公的妹妹穿的比我還像新娘夷蚊。我一直安慰自己构挤,他們只是感情好,可當我...
    茶點故事閱讀 67,228評論 6 388
  • 文/花漫 我一把揭開白布惕鼓。 她就那樣靜靜地躺著筋现,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上矾飞,一...
    開封第一講書人閱讀 51,190評論 1 299
  • 那天一膨,我揣著相機與錄音,去河邊找鬼洒沦。 笑死豹绪,一個胖子當著我的面吹牛,可吹牛的內(nèi)容都是我干的申眼。 我是一名探鬼主播瞒津,決...
    沈念sama閱讀 40,078評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼括尸!你這毒婦竟也來了巷蚪?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 38,923評論 0 274
  • 序言:老撾萬榮一對情侶失蹤濒翻,失蹤者是張志新(化名)和其女友劉穎屁柏,沒想到半個月后,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體有送,經(jīng)...
    沈念sama閱讀 45,334評論 1 310
  • 正文 獨居荒郊野嶺守林人離奇死亡前联,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,550評論 2 333
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了娶眷。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片似嗤。...
    茶點故事閱讀 39,727評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖届宠,靈堂內(nèi)的尸體忽然破棺而出烁落,到底是詐尸還是另有隱情,我是刑警寧澤豌注,帶...
    沈念sama閱讀 35,428評論 5 343
  • 正文 年R本政府宣布伤塌,位于F島的核電站,受9級特大地震影響轧铁,放射性物質(zhì)發(fā)生泄漏每聪。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 41,022評論 3 326
  • 文/蒙蒙 一齿风、第九天 我趴在偏房一處隱蔽的房頂上張望药薯。 院中可真熱鬧,春花似錦救斑、人聲如沸童本。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,672評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽穷娱。三九已至绑蔫,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間泵额,已是汗流浹背配深。 一陣腳步聲響...
    開封第一講書人閱讀 32,826評論 1 269
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留嫁盲,地道東北人篓叶。 一個月前我還...
    沈念sama閱讀 47,734評論 2 368
  • 正文 我出身青樓,卻偏偏與公主長得像亡资,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子向叉,可洞房花燭夜當晚...
    茶點故事閱讀 44,619評論 2 354

推薦閱讀更多精彩內(nèi)容