本文例程均為轉載
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)
}
}