gin框架中使用jwt来对前端用户身份认证

1,555 阅读2分钟

一、在gin中使用jwt-go来生成token

  • 1、安装包

    go get -u github.com/dgrijalva/jwt-go
    
  • 2、定义基础的结构体

    // 签名需要传递的参数
    type HmacUser struct {
    	Id       string `json:"id"`
    	Username string `json:"username"`
    }
    
    type MyClaims struct {
    	UserId   string `json:"user_id"`
    	Username string `json:"username"`
    	jwt.StandardClaims
    }
    
    // 登录的参数
    type LoginStruct struct {
    	Username string `json:"username"`
    	Password string `json:"password"`
    }
    
    // 证书签名密钥
    var jwtKey = []byte("abc")
    
  • 3、定义生成token的方法

    // 定义生成token的方法
    func generateToken(u HmacUser) (string, error) {
    	// 定义过期时间,7天后过期
    	expirationTime := time.Now().Add(7 * 24 * time.Hour)
    	claims := &MyClaims{
    		UserId:   u.Id,
    		Username: u.Username,
    		StandardClaims: jwt.StandardClaims{
    			ExpiresAt: expirationTime.Unix(), // 过期时间
    			IssuedAt:  time.Now().Unix(),     // 发布时间
    			Subject:   "token",               // 主题
    			Issuer:    "水痕",                  // 发布者
    		},
    	}
    	// 注意单词别写错了
    	token := jwt.NewWithClaims(jwt.SigningMethodHS256, claims)
    	tokenString, err := token.SignedString(jwtKey)
    	if err != nil {
    		return "", err
    	}
    	return tokenString, nil
    }
    
  • 4、定义一个路由来生成token

    router := gin.Default()
    router.POST("/login", func(c *gin.Context) {
      var loginStruct LoginStruct
      err := c.BindJSON(&loginStruct)
      if err != nil {
        fmt.Println("解析数据错误")
        c.JSON(http.StatusOK, gin.H{
          "code":    1,
          "message": "解析数据错误",
        })
        return
      }
      // 这里就不做查表操作验证用户名和密码
      fmt.Println("登录参数", loginStruct)
      // 传递给生成token的方法中
      userInfo := HmacUser{
        Id:       "1",
        Username: loginStruct.Username,
      }
      token, err := generateToken(userInfo)
      if err != nil {
        fmt.Println(err, "===")
        c.JSON(http.StatusOK, gin.H{
          "code":    1,
          "message": "生成token失败",
        })
        return
      }
      c.JSON(http.StatusOK, gin.H{
        "code":    0,
        "message": "登录成功",
        "token":   token,
      })
    })
    

二、使用中间件来验证客户端传递过来的token

  • 1、解析token

    // 定义解析token的方法
    func parseToken(tokenString string) (*jwt.Token, *MyClaims, error) {
    	claims := &MyClaims{}
    	token, err := jwt.ParseWithClaims(tokenString, claims, func(token *jwt.Token) (interface{}, error) {
    		return jwtKey, nil
    	})
    	return token, claims, err
    }
    
  • 2、在中间件中验证token

    // 定义中间件
    func AuthMiddleware() gin.HandlerFunc {
    	return func(c *gin.Context) {
    		// 从请求头中获取token
    		tokeString := c.GetHeader("token")
    		fmt.Println(tokeString, "当前token")
    		if tokeString == "" {
    			c.JSON(http.StatusOK, gin.H{
    				"code":    1,
    				"message": "必须传递token",
    			})
    			c.Abort()
    			return
    		}
    		token, claims, err := parseToken(tokeString)
    		if err != nil || !token.Valid {
    			c.JSON(http.StatusOK, gin.H{
    				"code":    1,
    				"message": "token解析错误",
    			})
    			c.Abort()
    			return
    		}
    		// 从token中解析出来的数据挂载到上下文上,方便后面的控制器使用
    		c.Set("userId", claims.UserId)
    		c.Set("userName", claims.Username)
    		c.Next()
    	}
    }
    
  • 3、定义路由使用中间件

    // 使用中间件
    router.POST("/check_token", AuthMiddleware(), func(c *gin.Context) {
      if userName, exists := c.Get("userName"); exists {
        fmt.Println("当前用户", userName)
      }
      c.JSON(http.StatusOK, gin.H{
        "code":    0,
        "message": "验证成功",
      })
    })