Gin通过jwt方式实现登录验证基本示例

本文使用Go Gin框架搭建的webserver示例,其中使用jwt进行登录验证,至于cookies session jwt token的区别,优缺点不再本位讨论范围内。

简单总结:使用jwt更方便,具有一定程度安全性,

具体使用Gin搭建webserver提供API服务部分就不赘述了, 可以参考其他博客介绍。完整代码在这里,

代码结构
Gin通过jwt方式实现登录验证基本示例_第1张图片

1 定义jwt格式部分所需信息和Gin middleware

在标准的Claims信息之外添加一些自定义信息

// 载荷,可添加自己需要的一些信息
type CustomClaims struct {
	UserId   int64  `json:"userId"`
	UserName string `json:"userName"`
	RoleId   string `json:"roleId"`
	jwt.StandardClaims
}

类似于java中过滤器

func JWTAuth() gin.HandlerFunc {
	return func(c *gin.Context) {
		//过滤是否验证token, login结构直接放行,这里为了简单起见,直接判断路径中是否带login,携带login直接放行
		if strings.Contains(c.Request.RequestURI, "login") {
			return
		}

		token := c.Request.Header.Get("token")
		if token == "" {
			c.JSON(http.StatusOK, gin.H{
				"status": -1,
				"msg":    "请求未携带token,无权限访问",
			})
			c.Abort()
			return
		}

		klog.Infof("gotten token:%s", token)

		j := NewJWT()
		// parse token, get the user and role info
		claims, err := j.ParseToken(token)
		if err != nil {
			if err == util.TokenExpired {
				c.JSON(http.StatusOK, gin.H{
					"status": -1,
					"msg":    "授权已过期",
				})
				c.Abort()
				return
			}
			c.JSON(http.StatusOK, gin.H{
				"status": -1,
				"msg":    err.Error(),
			})
			c.Abort()
			return
		}
		// 继续交由下一个路由处理,并将解析出的信息传递下去
		c.Set(util.Gin_Context_Key, claims)
	}
}

2 配置路由

/login接口不需要验证登录, 其他接口需要验证登录的token

func ConfigRouter(router *gin.Engine) {
	userController := mycontroller.NewUserController()
	//配置middleware
	router.Use(myjwt.JWTAuth())
	router.POST("/login", userController.Login)
	router.GET("/users", userController.GetAllUsers)
	router.GET("/usersfind", userController.FindUsers)
	router.GET("/users/:userId", userController.GetOneUser)
	router.PUT("/users", userController.CreateOneUser)
	router.POST("/users/:userId", userController.UpdateOneUser)
	router.DELETE("/users/:userId", userController.DeleteOneUser)
}

3 生成与解析token

生成token

func generateToken(c *gin.Context, user mydomain.User, roleId string, expiredTimeByMinute int64) {
	j := &myjwt.JWT{
		[]byte(util.SignKey),
	}
	claims := myjwt.CustomClaims{
		user.UserId,
		user.UserName,
		roleId,
		jwtgo.StandardClaims{
			NotBefore: int64(time.Now().Unix() - 1000),                   // 签名生效时间
			ExpiresAt: int64(time.Now().Unix() + expiredTimeByMinute*60), // 过期时间 一小时
			Issuer:    "ginjwtdemo",                                      //签名的发行者
		},
	}

	token, err := j.CreateToken(claims)

	if err != nil {
		c.JSON(http.StatusOK, gin.H{
			"status": -1,
			"msg":    err.Error(),
		})
		return
	}

	data := mydomain.LoginResp{
		Token: token,
	}
	c.JSON(http.StatusOK, gin.H{
		"status": 0,
		"msg":    "登录成功!",
		"data":   data,
	})
	return
}

解析token

func (j *JWT) ParseToken(tokenString string) (*CustomClaims, error) {
	token, err := jwt.ParseWithClaims(tokenString, &CustomClaims{}, func(token *jwt.Token) (interface{}, error) {
		return j.SigningKey, nil
	})
	if err != nil {
		if ve, ok := err.(*jwt.ValidationError); ok {
			if ve.Errors&jwt.ValidationErrorMalformed != 0 {
				return nil, util.TokenMalformed
			} else if ve.Errors&jwt.ValidationErrorExpired != 0 {
				// Token is expired
				return nil, util.TokenExpired
			} else if ve.Errors&jwt.ValidationErrorNotValidYet != 0 {
				return nil, util.TokenNotValidYet
			} else {
				return nil, util.TokenInvalid
			}
		}
	}
	if claims, ok := token.Claims.(*CustomClaims); ok && token.Valid {
		return claims, nil
	}
	return nil, util.TokenInvalid
}

4 效果演示

获取token
Gin通过jwt方式实现登录验证基本示例_第2张图片

在header中添加一个过期api的效果
Gin通过jwt方式实现登录验证基本示例_第3张图片

在header中没有token访问api的效果
Gin通过jwt方式实现登录验证基本示例_第4张图片

在header中添加一个当前有效的token访问api的效果
Gin通过jwt方式实现登录验证基本示例_第5张图片

你可能感兴趣的:(go,jwt)