这是我参与「第五届青训营 」伴学笔记创作活动的第 7 天。
讲一下jwt+中间件实现token鉴权:
1.jwt编码函数,通过编码具有声明的用户ID和用户名来生成JSON Web Token(JWT)。声明包括用户ID和用户名,到期时间(设置为当前时间24小时后),发行者名称(设置为“daniel”),不早于时间(设置为当前时间),以及其他元数据(如签发时间,预期的接收方,唯一标识符)。使用HMAC-SHA256算法与秘密密钥(keyinfo字节片)对JWT进行签名。生成的JWT作为字符串返回。如果在JWT生成期间发生错误,则会进行记录,并返回空字符串。:
func Encodetoken(userid string, username string) string {
keyinfo := []byte("3.1415926+0.618+qweasd")
temp := jwt.NewWithClaims(jwt.SigningMethodHS256, jwt.MapClaims{
"userid": userid,
"username": username,
// exp: jwt的过期时间,这个过期时间必须要大于签发时间
"exp": time.Now().Unix() + 3600*24,
// iss: jwt签发者
"iss": "daniel",
// nbf: 定义在什么时间之前,该jwt都是不可用的.
"nbf": time.Now().Unix(),
// sub: jwt所面向的用户
// aud: 接收jwt的一方
// iat: jwt的签发时间
// jti: jwt的唯一身份标识,主要用来作为一次性token,从而回避重放攻击。
})
token, err := temp.SignedString(keyinfo)
if err != nil {
log.Println(err.Error())
return ""
}
return token
}
2.解码函数,这个函数是用来解码JWT(JSON Web Token)令牌的。它需要一个字符串作为参数,即JWT令牌。该函数使用"3.1415926+0.618+qweasd"作为密钥,对令牌进行解码。如果解码成功,则将返回一个字符串数组,该数组包含JWT中的用户ID和用户名。如果解码失败,则返回一个包含两个空字符串的数组。:
// Decodetoken 此函数用于做jwt解码,返回解码后得到的用户id与用户名
func Decodetoken(token string) []string {
keyinfo := []byte("3.1415926+0.618+qweasd")
//parse函数会自动判断jwt令牌是否生效、过期等
parse, err := jwt.Parse(token, func(t *jwt.Token) (interface{}, error) {
return keyinfo, nil
})
if err != nil {
log.Println(err.Error())
return []string{"", ""}
}
return []string{parse.Claims.(jwt.MapClaims)["userid"].(string), parse.Claims.(jwt.MapClaims)["username"].(string)}
}
3.核验函数,这个函数检查请求中的token是否合法。它从请求参数中获取token字符串,并调用Decodetoken函数进行解码。如果解码失败,返回false和nil。如果解码成功,将用户ID和用户名从结果中取出,并在数据库中查找对应的用户信息。如果查询到的用户信息不为空,则说明token是合法的,返回true和该用户的信息;否则,返回false和nil。
func Checktoken(c *app.RequestContext) (bool, *user.Userinfo) {
token := c.Query("token")
p := Decodetoken(token)
if p[0] == "" {
return false, nil
}
userid, _ := strconv.Atoi(p[0])
var userinfo user.Userinfo
Connect2sql().Where("ID = ? AND username = ?", userid, p[1]).Find(&userinfo)
if userinfo.Username != "" {
return true, &userinfo
} else {
return false, nil
}
}
4.中间件,这个函数是一个Middleware,用于检查请求的token是否正确。如果正确,则将用户信息存储在请求上下文中,如果不正确,则返回错误信息,并终止请求的处理。:
func _infoMw() []app.HandlerFunc {
// your code
return []app.HandlerFunc{func(ctx context.Context, c *app.RequestContext) {
flag, userinfo := model.Checktoken(c)
if flag {
c.Set("userinfo", userinfo)
} else {
c.JSON(http.StatusOK, user.UserResponse{
Response: handler.Response{StatusCode: 1, StatusMsg: "User doesn't exist"},
})
c.Abort()
}
},
}
}
5.路由配置,这里只有login操作需要进行token鉴权,可以看到/douyin/user/login/路由中注册了刚刚实现的_loginMw()函数,表明通过/douyin/user/login/的请求都要先前往_loginMw()函数进行过滤。
func Register(r *server.Hertz) {
root := r.Group("/douyin", rootMw()...)
root.GET("/user/", append(_infoMw(), user.UserInfo)...)
root.POST("/user/register/", append(_registerMw(), user.Register)...)
root.POST("/user/login/", append(_loginMw(), user.Login)...)
}