GO語言Gin包:JWT使用demo


本文例程均為轉載

demo1

package main

import (
    "errors"
    "fmt"
    jwt "github.com/dgrijalva/jwt-go"
    "github.com/gin-gonic/gin"
    "net/http"
    "time"
)

//定義返回錯誤
const (
    ErrorServerBusy = "server is busy"
    ErrorReLogin = "relogin"
)

//Claim是一些實體(通常指的用戶)的狀態和額外的元數據
//Claims 結構體。需要內嵌jwt.StandardClaims。這個結構體是用來保存信息的
type JWTClaims struct {
    jwt.StandardClaims
    UserID int `json:"user_id"`
    Password string `json:"password"`
    Username string `json:"username"`
}

var (
    //自定義密鑰
    Secret = "123#111"  //salt
    //失效時間
    ExpireTime = 3600  //token expire time
)

func main() {
    r := gin.Default()
    r.GET("/login/:username/:password", login)
    r.GET("/verify/:token", verify)
    r.GET("/refresh/:token", refresh)
    r.GET("/sayHello/:token", sayHello)
    _ = r.Run(":8000")
}

//generate jwt token
//生成 jwt token
func genToken(claims *JWTClaims) (string, error) {
    //在 jwt 生成時使用 jwt.NewWithClaims 方法,需傳入 header claim 實例 和 密鑰。
    //jwt.SigningMethodHS256 
    token := jwt.NewWithClaims(jwt.SigningMethodHS256, claims)
    
    //加入密鑰生成最終token
    signedToken, err := token.SignedString([]byte(Secret))
    if err != nil {
        return "", errors.New(ErrorServerBusy)
    }
    return signedToken, nil
}

//登錄,獲取jwt token
func login(c *gin.Context) {
    username := c.Param("username")
    password := c.Param("password")
    claims := &JWTClaims{
        UserID: 1,
        Username: username,
        Password: password,
    }
    claims.IssuedAt = time.Now().Unix()
    //ExpireTime失效時間
    claims.ExpiresAt = time.Now().Add(time.Second * time.Duration(ExpireTime)).Unix()
    //生成token
    singedToken, err := genToken(claims)
    if err != nil {
        c.String(http.StatusNotFound, err.Error())
        return
    }
    c.String(http.StatusOK, singedToken)
}

//驗證jwt token
func verifyAction(strToken string) (*JWTClaims, error) {
    token, err := jwt.ParseWithClaims(strToken, &JWTClaims{}, func(token *jwt.Token) (interface{}, error) {
        return []byte(Secret), nil
    })
    if err != nil {
        return nil, errors.New(ErrorServerBusy)
    }

    claims, ok := token.Claims.(*JWTClaims)
    if !ok {
        return nil, errors.New(ErrorReLogin)
    }
    if err := token.Claims.Valid(); err != nil {
        return nil, errors.New(ErrorReLogin)
    }

    fmt.Println("verify")
    return claims, nil
}

func sayHello(c *gin.Context) {
    strToken := c.Param("token")
    claim, err := verifyAction(strToken)
    if err != nil {
        c.String(http.StatusNotFound, err.Error())
    }
    c.String(http.StatusOK, "hello, ", claim.Username)
}

func verify(c *gin.Context) {
    strToken := c.Param("token")
    claim, err := verifyAction(strToken)
    if err != nil {
        c.String(http.StatusNotFound, err.Error())
        return
    }
    c.String(http.StatusOK, "verify: ", claim.Username)
}

func refresh(c *gin.Context) {
    strToken := c.Param("token")
    claims, err := verifyAction(strToken)
    if err != nil {
        c.String(http.StatusNotFound, err.Error())
        return
    }
    claims.ExpiresAt = time.Now().Unix() + (claims.ExpiresAt - claims.IssuedAt)
    signedToken, err := genToken(claims)
    if err != nil {
        c.String(http.StatusNotFound, err.Error())
        return
    }
    c.String(http.StatusOK, signedToken, ", ", claims.ExpiresAt)
}

demo2

package main

import (
	"errors"
	"fmt"
	"net/http"
	"strings"
	"time"

	"github.com/dgrijalva/jwt-go"
	"github.com/gin-gonic/gin"
)

// MyClaims 自定義聲明結構體並內嵌jwt.StandardClaims
// jwt包自帶的jwt.StandardClaims只包含了官方字段
// 我們這里需要額外記錄一個username字段,所以要自定義結構體
// 如果想要保存更多信息,都可以添加到這個結構體中
type MyClaims struct {
	Username string `json:"username"`
	jwt.StandardClaims
}

type UserInfo struct {
	Username string `json:"username"`
	Password string `json:"password"`
}

//然后我們定義JWT的過期時間,這里以2小時為例:
const TokenExpireDuration = time.Hour * 2

//接下來還需要定義Secret
var MySecret = []byte("夏天夏天悄悄過去")

// GenToken 生成JWT
func GenToken(username string) (string, error) {
	// 創建一個我們自己的聲明
	c := MyClaims{
		"username", // 自定義字段
		jwt.StandardClaims{
			ExpiresAt: time.Now().Add(TokenExpireDuration).Unix(), // 過期時間
			Issuer:    "my-project",                               // 簽發人
		},
	}
	// 使用指定的簽名方法創建簽名對象
	token := jwt.NewWithClaims(jwt.SigningMethodHS256, c)
	// 使用指定的secret簽名並獲得完整的編碼后的字符串token
	return token.SignedString(MySecret)
}

// ParseToken 解析JWT
func ParseToken(tokenString string) (*MyClaims, error) {
	// 解析token
	token, err := jwt.ParseWithClaims(tokenString, &MyClaims{}, func(token *jwt.Token) (i interface{}, err error) {
		return MySecret, nil
	})
	if err != nil {
		return nil, err
	}
	if claims, ok := token.Claims.(*MyClaims); ok && token.Valid { // 校驗token
		return claims, nil
	}
	return nil, errors.New("invalid token")
}

//處理器
func authHandler(c *gin.Context) {
	// 用戶發送用戶名和密碼過來
	var user UserInfo
	err := c.ShouldBind(&user)
	if err != nil {
		c.JSON(http.StatusOK, gin.H{
			"code": 2001,
			"msg":  "無效的參數",
		})
		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
}

// JWTAuthMiddleware 基於JWT的認證中間件
func JWTAuthMiddleware() func(c *gin.Context) {
	return func(c *gin.Context) {
		// 客戶端攜帶Token有三種方式 1.放在請求頭 2.放在請求體 3.放在URI
		// 這里假設Token放在Header的Authorization中,並使用Bearer開頭
		// 這里的具體實現方式要依據你的實際業務情況決定
		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的函數來解析它
		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() // 后續的處理函數可以用過c.Get("username")來獲取當前請求的用戶信息
	}
}

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},
	})
}

func helloHandler(c *gin.Context) {
	c.JSON(http.StatusOK, gin.H{
		"message": "Hello www.topgoer.com",
	})
}

// SetupRouter 配置路由信息
func SetupRouter() *gin.Engine {
	r := gin.Default()
	r.GET("/topgoer", helloHandler)
	r.POST("/auth", authHandler)
	r.GET("/home", JWTAuthMiddleware(), homeHandler)
	return r
}

func main() {
	r := SetupRouter()
	if err := r.Run(); err != nil {
		fmt.Println("startup service failed, err:%v\n", err)
	}
}


免責聲明!

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



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