golang原生實(shí)現(xiàn)JWT的示例代碼
JWT(JSON Web Token)是一種基于JSON的安全令牌,可以用于在不同系統(tǒng)之間傳輸認(rèn)證信息。在Go中實(shí)現(xiàn)JWT驗(yàn)證,可以通過(guò)標(biāo)準(zhǔn)庫(kù)crypto/hmac、crypto/sha256和encoding/base64來(lái)編寫(xiě)自己的JWT。
獲取Token
我們?cè)诖朔庋b一個(gè)JWT的struct結(jié)構(gòu)體(由于除了Payload,其他很大可能不會(huì)在其他地方用到,所以不公開(kāi))
type JWT struct {
header string
Payload string
signature string
}將base64的編碼封裝一下方便使用
func encodeBase64(data string) string {
return base64.RawURLEncoding.EncodeToString([]byte(data))
}我們封裝一個(gè)用來(lái)生成簽名的方法
func generateSignature(key []byte, data []byte) (string, error) {
// 創(chuàng)建一個(gè)哈希對(duì)象
hash := hmac.New(sha256.New, key)
// 將要簽名的信息寫(xiě)入哈希對(duì)象中 hash.Write(data)
_, err := hash.Write(data)
if err != nil {
return "", err
}
// hash.Sum()計(jì)算簽名,在這里會(huì)返回簽名內(nèi)容
// 將簽名經(jīng)過(guò)base64編碼生成字符串形式返回。
return encodeBase64(string(hash.Sum(nil))), nil
}我們封裝一個(gè)CreateToken用于生成Token(該方法的參數(shù)key為(生成簽名所使用的密鑰))
func CreateToken(key []byte, payloadData any) (string, error) {
// 標(biāo)準(zhǔn)頭部
header := `{"alg":"HS256","typ":"JWT"}`
// 將負(fù)載的數(shù)據(jù)轉(zhuǎn)換為json
payload, jsonErr := json.Marshal(payloadData)
if jsonErr != nil {
return "", fmt.Errorf("負(fù)載json解析錯(cuò)誤")
}
// 將頭部和負(fù)載通過(guò)base64編碼,并使用.作為分隔進(jìn)行連接
encodedHeader := encodeBase64(header)
encodedPayload := encodeBase64(string(payload))
HeaderAndPayload := encodedHeader + "." + encodedPayload
// 使用簽名使用的key將傳入的頭部和負(fù)載連接所得的數(shù)據(jù)進(jìn)行簽名
signature, err := generateSignature(key, []byte(HeaderAndPayload))
if err != nil {
return "", err
}
// 將token的三個(gè)部分使用.進(jìn)行連接并返回
return HeaderAndPayload + "." + signature, nil
}解析Token
我們封裝一個(gè)解析token的方法
func ParseJwt(token string, key []byte) (*JWT, error) {
// 分解規(guī)定,我們使用.進(jìn)行分隔,所以我們通過(guò).進(jìn)行分隔成三個(gè)字符串的數(shù)組
jwtParts := strings.Split(token, ".")
// 數(shù)據(jù)數(shù)組長(zhǎng)度不是3就說(shuō)明token在格式上就不合法
if len(jwtParts) != 3 {
return nil, fmt.Errorf("非法token")
}
// 分別拿出
encodedHeader := jwtParts[0]
encodedPayload := jwtParts[1]
signature := jwtParts[2]
// 使用key將token中的頭部和負(fù)載用.連接后進(jìn)行簽名
// 這個(gè)簽名應(yīng)該個(gè)token中第三部分的簽名一致
confirmSignature, err := generateSignature(key, []byte(encodedHeader+"."+encodedPayload))
if err != nil {
return nil, fmt.Errorf("生成簽名錯(cuò)誤")
}
// 如果不一致
if signature != confirmSignature {
return nil, fmt.Errorf("token驗(yàn)證失敗")
}
// 將payload解base64編碼
dstPayload, _ := base64.RawURLEncoding.DecodeString(encodedPayload)
// 返回我們的JWT對(duì)象以供后續(xù)使用
return &JWT{encodedHeader, string(dstPayload), signature}, nil
}實(shí)際使用
我們構(gòu)造一個(gè)用戶的結(jié)構(gòu)體
type UserInfo struct {
Name string `json:"name"`
Password string `json:"password"`
}我們這次使用123456作為密鑰簡(jiǎn)單的驗(yàn)證一下
var Key []byte = []byte("12346")在此我們構(gòu)造一個(gè)驗(yàn)證的中間件
func jwtConfirm(context *gin.Context) {
// 登錄不需要token
if context.Request.RequestURI == "/login" {
return
}
// 拿出token
token := context.GetHeader("Token")
// 進(jìn)行解析驗(yàn)證
jwt, err := utils.ParseJwt(token, Key)
if err != nil {
context.JSON(200, gin.H{
"msg": err.Error(),
})
// 有問(wèn)題就流產(chǎn)掉(我也不知道怎么翻譯好了,香蕉貓.jpg)
context.Abort()
}
// 驗(yàn)證通過(guò)就將負(fù)載返回回去
context.JSON(200, gin.H{
"payload": jwt.Payload,
})
}我們?cè)诖嘶A(chǔ)上就可以使用了,這邊使用gin框架簡(jiǎn)單的測(cè)試一下
func main() {
// 使用默認(rèn)路由
router := gin.Default()
// 注冊(cè)中間件
router.Use(jwtConfirm)
// 簡(jiǎn)單做兩個(gè)服務(wù)
router.POST("/login", func(context *gin.Context) {
// 接收用戶參數(shù)
var userInfo UserInfo = UserInfo{}
// 使用jsonbind接收
bindErr := context.ShouldBindJSON(&userInfo)
if bindErr != nil {
context.JSON(200, gin.H{
"msg": bindErr.Error(),
})
}
// 使用密鑰做出token
jwt, err := utils.CreateJwt(Key, userInfo)
if err != nil {
fmt.Println(err)
}
// 我們將token直接返回用于測(cè)試
context.JSON(200, gin.H{
"token": jwt,
})
})
router.GET("/doing")
router.Run()
}測(cè)試結(jié)果

我們拿到了token
我們現(xiàn)在去試一下如果不帶token的結(jié)果

我們?cè)囈幌聰y帶錯(cuò)誤token的情況

我們最后測(cè)試一下正確的token

結(jié)語(yǔ)
在實(shí)際環(huán)境中會(huì)使用更復(fù)雜的情況進(jìn)行使用(例如密鑰會(huì)更加復(fù)雜,會(huì)在pyload中設(shè)置失效時(shí)間等等),但是生成token和解析token的操作和上述的操作差別不大
到此這篇關(guān)于golang原生實(shí)現(xiàn)JWT的示例代碼的文章就介紹到這了,更多相關(guān)golang原生實(shí)現(xiàn)JWT內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
深入探究Golang中l(wèi)og標(biāo)準(zhǔn)庫(kù)的使用
Go?語(yǔ)言標(biāo)準(zhǔn)庫(kù)中的?log?包設(shè)計(jì)簡(jiǎn)潔明了,易于上手,可以輕松記錄程序運(yùn)行時(shí)的信息、調(diào)試錯(cuò)誤以及跟蹤代碼執(zhí)行過(guò)程中的問(wèn)題等。本文主要來(lái)深入探究?log?包的使用和原理,幫助讀者更好地了解和掌握它2023-05-05
一文教你學(xué)會(huì)Go中singleflight的使用
緩存在項(xiàng)目中使用應(yīng)該是非常頻繁的,提到緩存只要了解過(guò)?singleflight?,基本都會(huì)用于緩存實(shí)現(xiàn)的一部分吧,下面就跟隨小編一起來(lái)學(xué)習(xí)一下singleflight的使用吧2024-02-02
Golang編程實(shí)現(xiàn)刪除字符串中出現(xiàn)次數(shù)最少字符的方法
這篇文章主要介紹了Golang編程實(shí)現(xiàn)刪除字符串中出現(xiàn)次數(shù)最少字符的方法,涉及Go語(yǔ)言字符串遍歷與運(yùn)算相關(guān)操作技巧,需要的朋友可以參考下2017-01-01
Go?語(yǔ)言數(shù)據(jù)結(jié)構(gòu)如何實(shí)現(xiàn)抄一個(gè)list示例詳解
這篇文章主要為大家介紹了Go?語(yǔ)言數(shù)據(jù)結(jié)構(gòu)如何實(shí)現(xiàn)抄一個(gè)list示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2023-04-04
golang 流式讀取和發(fā)送使用場(chǎng)景示例
這篇文章主要為大家介紹了golang 流式讀取和發(fā)送使用場(chǎng)景示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2023-12-12
grpcurl通過(guò)命令行訪問(wèn)gRPC服務(wù)
這篇文章主要為大家介紹了grpcurl通過(guò)命令行訪問(wèn)gRPC服務(wù)示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2022-06-06
Golang?Mutex錯(cuò)過(guò)會(huì)后悔的重要知識(shí)點(diǎn)分享
互斥鎖?Mutex?是并發(fā)控制的一個(gè)基本手段,是為了避免并發(fā)競(jìng)爭(zhēng)建立的并發(fā)控制機(jī)制,本文主要為大家整理了一些Mutex的相關(guān)知識(shí)點(diǎn),希望對(duì)大家有所幫助2023-07-07

