JWT是什么
先来介绍一下什么是cookie和session:
cookie
由服务器生成,发送给浏览器,浏览器以键值对的方式保存下来,下次发送请求的时候带上cookie保存的信息传给客服务器。
缺点:每个域名下可使用数量少,大小也有限制。
session
由服务器生成,服务器保存主体信息,会发送一个sessionid给客户端cookie保存,下次发送请求时带上sessionid传给服务端,服务端根据sessionid和主体信息进行对比验证。
缺点:
cookie+session在跨域场景很麻烦;
如果是分布式部署,需要做多机共享session机制;
基于cookie的机制容易被CSRF;
查询session信息可能会有数据库查询操作,带来性能问题。
JWT
和token有点类似,在服务端加入了一个secret密钥,由用户发送用户名密码给服务端,服务端验证,成功之后就生成三个部分header,payload,signature组成的jwt token给客户端,之后的请求都带上 jwt token,服务端通过secret密钥进行验证。
jwt库的用法
1.生成jwt token
type MyClaims struct {
Username string `json:"username"`
jwt.StandardClaims
}
const TokenExpireDuration = time.Hour * 24 //设置过期时间
var Secret = []byte("secret") //密码自行设定
func GenToken(username string) (string, error) {
// 创建一个我们自己的声明
c := MyClaims{
username, // 自定义字段
jwt.StandardClaims{
ExpiresAt: time.Now().Add(TokenExpireDuration).Unix(), // 过期时间
Issuer: "superxon", // 签发人
},
}
// 使用指定的签名方法创建签名对象
token := jwt.NewWithClaims(jwt.SigningMethodHS256, c)
// 使用指定的secret签名并获得完整的编码后的字符串token
return token.SignedString(Secret)
}
2.将生成的token返回给客户端
type Profile struct {
Username string `db:"username"`
Password string `db:"password"`
}
func AuthLoginHandler(c *gin.Context) {
// 用户发送用户名和密码过来
var user User.Profile
err := c.ShouldBindJSON(&user)
if err != nil {
c.JSON(http.StatusBadRequest, gin.H{
"code": 2001,
"msg": "无效的参数",
})
return
}
// 校验用户名和密码是否正确
_, err := getProfile(user.Username)
if err != nil {
c.JSON(http.StatusNotFound, gin.H{
"code": 2003,
"msg": "用户不存在",
})
return
}
tokenString, _ := Middlewares.GenToken(user.Username)
c.JSON(http.StatusOK, gin.H{
"code": 2000,
"msg": "success",
"Token": tokenString,
"username": profile.Username,
})
}
3.解析客户端中带token的请求,通过 authorization字段保存jwt token
// ParseToken 解析JWT
func ParseToken(tokenString string) (*MyClaims, error) {
// 解析token
token, err := jwt.ParseWithClaims(tokenString, &MyClaims{}, func(token *jwt.Token) (i interface{}, err error) {
return MySecret, nil
})
if err != nil {
return nil, err
}
if claims, ok := token.Claims.(*MyClaims); ok && token.Valid { // 校验token
return claims, nil
}
return nil, errors.New("invalid token")
}
4.定义中间件函数
// JWTAuthMiddleware 基于JWT的认证中间件--验证用户是否登录
func JWTAuthMiddleware() func(c *gin.Context) {
return func(c *gin.Context) {
authHeader := c.Request.Header.Get("authorization")
if authHeader == "" {
c.JSON(http.StatusUnauthorized, gin.H{
"code": 2003,
"msg": "请求头中auth为空",
})
c.Abort()
return
}
// 按空格分割
parts := strings.Split(authHeader, ".")
if len(parts) != 3 {
c.JSON(http.StatusUnauthorized, gin.H{
"code": 2004,
"msg": "请求头中auth格式有误",
})
c.Abort()
return
}
mc, ok := ParseToken(authHeader, Secret)
if ok == false {
c.JSON(http.StatusUnauthorized, gin.H{
"code": 2005,
"msg": "无效的Token",
})
c.Abort()
return
}
m := mc.(jwt.MapClaims)
// 将当前请求的username信息保存到请求的上下文c上
c.Set("username", m["username"])
c.Next() // 后续的处理函数可以用过c.Get("username")来获取当前请求的用户信息
}
}
拓展
gin框架常用中间件还有SHA1加密,限流中间件等,后续再进行深入学习。