Gin源码分析 – 中间件 – JWT认证机制
•
Gin框架
Github地址:https://github.com/golang-jwt/jwt
文档地址:https://pkg.go.dev/github.com/golang-jwt/jwt
下载:go get github.com/golang-jwt/jwt
签发token和验证token
package main import ( "errors" "fmt" "github.com/golang-jwt/jwt" "time" ) // 第一步:定义结构体 // MyClaims 定义结构体并继承jwt.StandardClaims // jwt包自带的jwt.StandardClaims只包含了官方字段 // 我们需要额外记录一个username和id字段,所以要自定义结构体 // 如果想要保存更多信息,都可以添加到这个结构体中 type MyCustomClaims struct { Id int `json:"id"` Username string `json:"username"` jwt.StandardClaims } // 定义加密秘钥 var mySigningKey = []byte("lqzisnb") func genToken(claims MyCustomClaims) (string, error) { // 使用HS256加密方式 token := jwt.NewWithClaims(jwt.SigningMethodHS256, claims) signToken, err := token.SignedString(mySigningKey) if err != nil { return "", err } return signToken, nil } func parserToken(signToken string) (*MyCustomClaims, error) { var claims MyCustomClaims token, err := jwt.ParseWithClaims(signToken, &claims, func(token *jwt.Token) (interface{}, error) { return mySigningKey, nil }) if token.Valid { return &claims, nil } else { return nil, err } } // 带详细错误的解析 func parserTokenWithError(signToken string) (*MyCustomClaims, error) { var claims MyCustomClaims token, err := jwt.ParseWithClaims(signToken, &claims, func(token *jwt.Token) (interface{}, error) { return mySigningKey, nil }) if token.Valid { return &claims, nil } else if ve, ok := err.(*jwt.ValidationError); ok { if ve.Errors&jwt.ValidationErrorMalformed != 0 { return nil, errors.New("不是一个合法的token") } else if ve.Errors&(jwt.ValidationErrorExpired|jwt.ValidationErrorNotValidYet) != 0 { return nil, errors.New("token过期了") } else { fmt.Println("Couldn't handle this token:", err) return nil, errors.New("无法处理这个token") } } else { return nil, errors.New("无法处理这个token") } } func main() { claims := MyCustomClaims{ 1, "lqz", jwt.StandardClaims{ ExpiresAt: time.Now().Add(7 * time.Hour).Unix(), // 过期时间7小时 Issuer: "lqz", // 签发人 }, } signToken, _ := genToken(claims) fmt.Println(signToken) c, err := parserToken(signToken) fmt.Println(c, err) }
Gin框架中集成
package main import ( "fmt" "github.com/gin-gonic/gin" "github.com/golang-jwt/jwt" "net/http" "time" ) // 第一步:定义结构体 // MyClaims 定义结构体并继承jwt.StandardClaims // jwt包自带的jwt.StandardClaims只包含了官方字段 // 我们需要额外记录一个username和id字段,所以要自定义结构体 // 如果想要保存更多信息,都可以添加到这个结构体中 type MyCustomClaims struct { Id int `json:"id"` Username string `json:"username"` jwt.StandardClaims } // 定义加密秘钥 var mySigningKey = []byte("lqzisnb") func genToken(claims MyCustomClaims) (string, error) { // 使用HS256加密方式 token := jwt.NewWithClaims(jwt.SigningMethodHS256, claims) signToken, err := token.SignedString(mySigningKey) if err != nil { return "", err } return signToken, nil } func parserToken(signToken string) (*MyCustomClaims, error) { var claims MyCustomClaims token, err := jwt.ParseWithClaims(signToken, &claims, func(token *jwt.Token) (interface{}, error) { return mySigningKey, nil }) if token.Valid { return &claims, nil } else { return nil, err } } // 基于JWT的认证中间件 func JWTAuthMiddleware(c *gin.Context) { // 从请求头中取出 signToken := c.Request.Header.Get("Authorization") if signToken == "" { c.JSON(http.StatusOK, gin.H{ "code": 1002, "msg": "token为空", }) c.Abort() return } // 校验token myclaims, err := parserToken(signToken) if err != nil { fmt.Println(err) c.JSON(http.StatusOK, gin.H{ "code": 1003, "msg": "token校验失败", }) c.Abort() return } // 将用户的id放在到请求的上下文c上 c.Set("userid", myclaims.Id) c.Next() // 后续的处理函数可以用过c.Get("userid")来获取当前请求的id } func main() { r := gin.Default() r.POST("/login", func(c *gin.Context) { username := c.PostForm("username") password := c.PostForm("password") if username == "lqz" && password == "123" { token, _ := genToken(MyCustomClaims{1, "lqz", jwt.StandardClaims{ ExpiresAt: time.Now().Add(7 * time.Hour).Unix(), // 过期时间 Issuer: "lqz", // 签发人 }}) c.JSON(200, gin.H{"code": "100", "msg": "登陆成功", "token": token}) } else { c.JSON(200, gin.H{"code": "101", "msg": "用户名或密码错误"}) } }) // 该接口登陆后才能访问,加中间件 r.GET("/home", JWTAuthMiddleware, func(c *gin.Context) { fmt.Println(c.Get("userid")) c.JSON(200, gin.H{"code": 100, "msg": "home"}) }) r.Run(":8080") }