JSON Web Token(JWT)是一个开放标准(RFC 7519),它定义了一种方式,用于在各方之间安全地将信息作为 JSON 对象传输。由于此信息是经过数字签名的,因此可以被验证和信任。通过设置获取相关加密 token 是一个很方便获取检验登录状态的方法。基于项目开发,下面介绍 jwt-go 库完成签名与设置登录状态中间件的方法。
安装 jwt-go
go get -u github.com/dgrijalva/jwt-go@v3.2.0
生成 token
要检验之前首先需要创建一个用于检验的 token 来提供搜索判断。说到底其实 token 就是一个经过加密后生成的字符串,我们这里采用给定一个用户信息结构体来生成 token. 首先定义 token 所加密解密对应的结构体 MyClaims
type MyClaims struct {
UserId string `json:"userid"`
Password string `json:"password"`
jwt.RegisteredClaims
}
其中的 jwt.RegisteredClaims 是一个记录 jwt 信息的结构体,包括注册信息,创建者信息等等。
基于 jwt-go 库的封装,生成 token 的过程十分的简单,只需要根据用户信息初始化一个 Mycliams 结构体,然后调用函数创建即可,具体代码示例如下:
var MySecret = []byte("字节后端小分队") // 用于签名的特殊字段
func CreateToken(userId string, password string) (string, error) {
// 根据用户信息初始胡一个 MyClaims
claim := MyClaims{
UserId: userId,
Password: password,
RegisteredClaims: jwt.RegisteredClaims{
ExpiresAt: jwt.NewNumericDate(time.Now().Add(time.Hour * time.Duration(1) * 8)),
IssuedAt: jwt.NewNumericDate(time.Now()),
NotBefore: jwt.NewNumericDate(time.Now()),
Issuer: "字节后端小分队",
},
}
// 根据用户信息创建一个 token
token := jwt.NewWithClaims(jwt.SigningMethodHS256, claim)
token_str, err := token.SignedString(MySecret)
return token_str, err
}
其中的加密算法具体实现我们不需要关心,从使用者层面加密成功了一个唯一 token 就可以了。
解密 token
除了根据给定用户信息生成一个 token,要实现登录查询我们还要完成解密,也就是给定一个 token 我们需要唯一解密出这里面的用户信息。jwt-go 库也已经为我们封装好了解密算法,只要直接调用就可以根据给定 token 字符串来唯一反解出我们需要的用户信息了。代码示例如下
func Gettoken(token string) (*MyClaims, error) {
// 解密过程
mytoken, err := jwt.ParseWithClaims(token, &MyClaims{}, func(token *jwt.Token) (interface{}, error) {
return MySecret, nil
})
// 解密成功则正常返回
if mytoken != nil {
if claims, ok := mytoken.Claims.(*MyClaims); ok && mytoken.Valid {
return claims, nil
}
}
// 否则返回一个错误
return nil, err
}
使用 jwt 实现用户登录状态检验的中间件
首先得定义一个结构体来实现对 token 字符串的存储与检索,主要用作检验登录状态,其如果能被检索到那么就证明已经登陆:
type Response struct {
StatusCode int32 `json:"status_code"`
StatusMsg string `json:"status_msg,omitempty"`
}
type UserLoginResponse struct {
Response
UserId int64 `json:"user_id,omitempty"`
Token string `json:"token"`
}
Response 是用于判断路由返回状态的,在注册时,一个 UserLoginResponse 结构体会作为 json 文件写入记录,在已经登录状态下使用时调用 c.PostForm("token") 即可获得对应 token 值。
基于上面的架构,我们要判断当前是否有用户登录以及登录用户信息就可以使用一个 token 权鉴的获取来实现。基于此我们可以把这个判断过程实现为一个中间件,作为所以需要进行登录状态判断路由的一个预处理。代码示例如下:
func Jwt2r() gin.HandlerFunc {
return func(c *gin.Context) {
// 获取 token
token := c.PostForm("token")
if token == "" {
token = c.Query("token")
}
// 获取失败证明未登录
if token == "" {
c.JSON(http.StatusBadRequest, entity.Response{
StatusCode: 1, StatusMsg: "Not login",
})
return
}
// 异常判断,防止传入 token 不合法
_, errToken := Gettoken(token)
if errToken != nil {
c.JSON(http.StatusBadRequest, entity.Response{
StatusCode: 1, StatusMsg: errToken.Error(),
})
return
}
// 执行事务
c.Next()
}
}
以上就是一个中间件的简单样例,想要获取用户信息,除了解析 token,还可以直接调用方法 c.PostForm("user_id").
总结
以上就是本次项目对 jwt 实现数字签名的一个简单实现,具体的 jwt 文件细节还有待后续学习。