1.jwt使用
安装
go get github.com/golang-jwt/jwt/v4
默认Claim
如果我们直接使用JWT中默认的字段,没有其他定制化的需求则可以直接使用这个包中的和方法快速生成和解析token。
// 用于签名的字符串
var mySigningKey = []byte("liwenzhou.com")// GenRegisteredClaims 使用默认声明创建jwt
func GenRegisteredClaims() (string, error) {// 创建 Claimsclaims := &jwt.RegisteredClaims{ExpiresAt: jwt.NewNumericDate(time.Now().Add(time.Hour * 24)), // 过期时间Issuer: "qimi", // 签发人}// 生成token对象token := jwt.NewWithClaims(jwt.SigningMethodHS256, claims)// 生成签名字符串return token.SignedString(mySigningKey)
}// ParseRegisteredClaims 解析jwt
func ValidateRegisteredClaims(tokenString string) bool {// 解析tokentoken, err := jwt.Parse(tokenString, func(token *jwt.Token) (interface{}, error) {return mySigningKey, nil})if err != nil { // 解析token失败return false}return token.Valid
}
自定义Claims
我们需要定制自己的需求来决定JWT中保存哪些数据,比如我们规定在JWT中要存储username
信息,那么我们就定义一个MyClaims
结构体如下:
// CustomClaims 自定义声明类型 并内嵌jwt.RegisteredClaims
// jwt包自带的jwt.RegisteredClaims只包含了官方字段
// 假设我们这里需要额外记录一个username字段,所以要自定义结构体
// 如果想要保存更多信息,都可以添加到这个结构体中
type CustomClaims struct {// 可根据需要自行添加字段Username string `json:"username"`jwt.RegisteredClaims // 内嵌标准的声明
}
然后我们定义JWT的过期时间,这里以24小时为例:
const TokenExpireDuration = time.Hour * 24
接下来还需要定义一个用于签名的字符串:
// CustomSecret 用于加盐的字符串
var CustomSecret = []byte("夏天夏天悄悄过去")
生成JWT
我们可以根据自己的业务需要封装一个生成 token 的函数。
// GenToken 生成JWT
func GenToken(username string) (string, error) {// 创建一个我们自己的声明claims := CustomClaims{username, // 自定义字段jwt.RegisteredClaims{ExpiresAt: jwt.NewNumericDate(time.Now().Add(TokenExpireDuration)),Issuer: "my-project", // 签发人},}// 使用指定的签名方法创建签名对象token := jwt.NewWithClaims(jwt.SigningMethodHS256, claims)// 使用指定的secret签名并获得完整的编码后的字符串tokenreturn token.SignedString(CustomSecret)
}
解析JWT
根据给定的 JWT 字符串,解析出数据。
// ParseToken 解析JWT
func ParseToken(tokenString string) (*CustomClaims, error) {// 解析token// 如果是自定义Claim结构体则需要使用 ParseWithClaims 方法token, err := jwt.ParseWithClaims(tokenString, &CustomClaims{}, func(token *jwt.Token) (i interface{}, err error) {// 直接使用标准的Claim则可以直接使用Parse方法//token, err := jwt.Parse(tokenString, func(token *jwt.Token) (i interface{}, err error) {return CustomSecret, nil})if err != nil {return nil, err}// 对token对象中的Claim进行类型断言if claims, ok := token.Claims.(*CustomClaims); ok && token.Valid { // 校验tokenreturn claims, nil}return nil, errors.New("invalid token")
}
2 gin中使用JWT
若不想自己处理可以采用三方jwt库:https://github.com/appleboy/gin-jwt。
首先我们注册一条路由/auth
,对外提供获取Token的渠道:
r.POST("/auth", authHandler)
我们的authHandler
定义如下:
func authHandler(c *gin.Context) {// 用户发送用户名和密码过来var user UserInfoerr := c.ShouldBind(&user)if err != nil {c.JSON(http.StatusOK, gin.H{"code": 2001,"msg": "无效的参数",})return}// 校验用户名和密码是否正确if user.Username == "q1mi" && user.Password == "q1mi123" {// 生成TokentokenString, _ := 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
}
用户通过上面的接口获取Token之后,后续就会携带着Token再来请求我们的其他接口,这个时候就需要对这些请求的Token进行校验操作了,很显然我们应该实现一个检验Token的中间件,具体实现如下:
// 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")来获取当前请求的用户信息}
}
注册一个/home
路由,发个请求验证一下吧。
r.GET("/home", JWTAuthMiddleware(), homeHandler)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},})
}