jwt是什么
jwt全称 JSON Web Token,是一种跨域认证的解决方案。
jwt由三部分组成,中间使用.进行分割,格式如下:
header.payload.signature
- header: 是一个JSON对象,包含如下内容
{ "typ": "JWT",//默认为JWT "alg": "HS256"//支持多种加密算法 } - payload: 也是一个JSON对象,用来存放实际所需的数据。包括如下JWT标准提供的7种可选数据和自定义数据
| 字段 | 描述 |
|---|---|
| iss | 签发者 |
| sub | 主题,用于鉴别一个用户 |
| exp | 过期时间 |
| aud | 受众 |
| iat | 签发时间 |
| nbf | 生效时间 |
| jti | jwt ID |
- signature: 签名,将header和payload进行Base64URL编码后,再用指定密钥和header中的加密算法进行加密得到
jwt的特点
相较于传统的Cookie和Session的认证方式,jwt不需要像Session那样在服务器上存储信息,也没有Cookie的大小限制,更加灵活,也更适用于分布式系统。
在gin中使用jwt
使用jwt-go包
go get -u github.com/golang-jwt/jwt/v4
import "github.com/golang-jwt/jwt/v4"
token生成函数和解析函数
在样例中,我们在payload中存储用户id,读者可根据自己需要添加任意信息。
package util
import (
"errors"
"github.com/golang-jwt/jwt/v4"
"log"
"time"
)
// MyClaims 自定义的payload
type MyClaims struct {
jwt.RegisteredClaims // 内置的payload字段
UserID string `json:"user_id"` // 自定义的payload字段
}
type JWT struct {
}
var key = []byte("qwouidfoqiwr23fioqw") // 用于签名的密钥
func (j *JWT) GenerateToken(msg string, ttl int64) string {
now := time.Now()
// 创建一个新的token
token := jwt.NewWithClaims(jwt.SigningMethodHS256, &MyClaims{
UserID: msg,
RegisteredClaims: jwt.RegisteredClaims{
ExpiresAt: jwt.NewNumericDate(now.Add(time.Duration(24*ttl) * time.Hour)), // 过期时间,ttl天后过期
IssuedAt: jwt.NewNumericDate(now), // 签发时间
NotBefore: jwt.NewNumericDate(now), // 生效时间
Issuer: "tiktok-lite", // 签发人
},
})
tokenString, _ := token.SignedString(key) // 签名, 返回token字符串
return tokenString
}
func (j *JWT) ParseToken(tokenString string) (string, error) {
token, err := jwt.ParseWithClaims(tokenString, &MyClaims{}, func(token *jwt.Token) (interface{}, error) {
return key, nil
})
if err != nil {
log.Println(err)
return "", errors.New("invalid token")
}
claims, ok := token.Claims.(*MyClaims)
if !ok || !token.Valid {
log.Println("invalid token")
return "", errors.New("invalid token")
}
return claims.UserID, nil
}
gin中间件
构造一个jwt鉴权中间件,在需要用户鉴权的路由中使用。
一般token信息会放在请求的header的Authorization字段中,并以Bearer +token字符串的方式发送。本文采用URL参数的方式发送token,例如:http://domain.com/user/info?user_id=xxxxx&token=xxxxxxx
func JWTAuth() func(c *gin.Context) {
return func(c *gin.Context) {
userId := c.Query("user_id")
token := c.Query("token")
if userId == "" || token == "" {
c.JSON(200, &controller.UserInfoRes{
Code: -1,
Msg: "缺失参数",
User: nil,
})
c.Abort()
return
}
jwt := util.JWT{} // 上文实现的JWT
claims, err := jwt.ParseToken(token)
if err != nil || claims != userId {
c.JSON(200, &controller.UserInfoRes{
Code: -1,
Msg: "token验证失败",
User: nil,
})
c.Abort()
return
}
c.Next()
}
}
login时生成token
func Login(username string, password string) (*UserToken, error) {
...
jwt := util.JWT{}
token := jwt.GenerateToken(strconv.Itoa(int(data[0].ID)), 1)
return &UserToken{
...
Token: token,
}, nil
}
使用中间件
user := r.Group("/user")
user.GET("/info", middleware.JWTAuth(), func(c *gin.Context) {
userID := c.Query("user_id")
userInfo := controller.GetUserInfo(userID)
c.JSON(200, userInfo)
})