Jwt 获取登录状态的中间件设置 | 青训营

81 阅读3分钟

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 文件细节还有待后续学习。