Go 中实现 JWT 登录 | 臭大佬

臭大佬 2020-08-22 14:41:25 4145
Go 
简介 Go 中实现 JWT 登录

什么是JWT

Json web token (JWT), 是为了在网络应用环境间传递声明而执行的一种基于JSON的开放标准((RFC 7519).该token被设计为紧凑且安全的,特别适用于分布式站点的单点登录(SSO)场景。JWT的声明一般被用来在身份提供者和服务提供者间传递被认证的用户身份信息,以便于从资源服务器获取资源,也可以增加一些额外的其它业务逻辑所必须的声明信息,该token也可直接被用于认证,也可被加密。

相信很多朋友在写后台接口的时候都有用过,最近想把博客的后端用 beego框架来实现,而后台模块接口都是需要登录后才能访问的,这就需要用的JWT了,go中相应的包有github.com/dgrijalva/jwt-go,下面简单介绍一下实现过程。

本文是在 beego框架下实现的,如果您用的不是beego框架,逻辑是差不多的,请根据具体情况修改代码。

安装扩展

执行命令:

go get github.com/dgrijalva/jwt-go

除了JWT包,这里再推荐一个包github.com/syyongx/php2go,这个包让go代码敲起来像php,包里面实现了很多类似于php函数的方法,对于php程序员可是福音啊,敲起代码一把梭。墙裂推荐。

go get github.com/syyongx/php2go

实现过程

我在 mian.go的init方法中引入了中间件方法,

middlewares.Init()中实现了过滤器功能,当路由进来首先会去判断是否匹配正则表达式”/api/*”,

如果匹配成功,会去判断是否在开放路由数组中。不在的话去验证token,验证成功则可以返回数据,否则无法获取数据。从未实现登录验证逻辑。

生成 token

生成token是在用户登录的时候,通过验证账号密码是否正确进行发放token。

import (
    ...
    "github.com/dgrijalva/jwt-go"
)

//用户登录
func (self *AdminController) Login() {
    // 用户账号密码验证逻辑
    ...
    // 带权限创建令牌
    claims := make(jwt.MapClaims)
    claims["account"] = admin.Account
    claims["username"] = admin.Username
    claims["tel"] = admin.Tel
    claims["email"] = admin.Email
    claims["exp"] = time.Now().Add(time.Hour * 2).Unix() //2小时有效期,过期需要重新登录获取token
    token := jwt.NewWithClaims(jwt.SigningMethodHS256, claims)
    // 使用自定义字符串加密 and get the complete encoded token as a string
    tokenString, err := token.SignedString([]byte("mykey"))
    if err != nil {
        return
    }
    jsonData := make(map[string]interface{}, 4)
    jsonData["token"] = tokenString
    jsonData["id"] = admin.Id
    jsonData["account"] = admin.Account
    jsonData["username"] = admin.Username
    jsonData["tel"] = admin.Tel
    jsonData["email"] = admin.Email
    // 返回带token的数据
    self.ResJson(0, "获取成功", jsonData, nil)
}

调用

helper := commons.Helper{}
token, e := helper.ParseToken(ctx) // ctx *context.Context
if e != nil {
    return
}
claims, ok := token.Claims.(jwt.MapClaims)
if !ok {
    // 验证失败
    return
}

验证 token

/**
 * Description:
 * User: Vijay <1937832819@qq.com>
 * Date: 2020/08/21
 * Time: 17:40
 */

package commons

import (
    "github.com/astaxie/beego"
    "github.com/astaxie/beego/context"
    "github.com/dgrijalva/jwt-go"
    "strings"
)

type Helper struct {
}

/**
 wjt token 验证
 */
func (h *Helper) ParseToken(ctx *context.Context) (t *jwt.Token, e interface{}) {
    authString := ctx.Input.Header("Authorization")
    kv := strings.Split(authString, " ")
    if len(kv) != 2 || kv[0] != "Bearer" {
        return nil, "AuthString无效"
    }
    tokenString := kv[1]
    // Parse token
    token, err := jwt.Parse(tokenString, func(token *jwt.Token) (interface{}, error) {
        return []byte("mykey"), nil
    })
    if err != nil {
        if ve, ok := err.(*jwt.ValidationError); ok {
            if ve.Errors&jwt.ValidationErrorMalformed != 0 {
                // That's not even a token
                return nil, err
            } else if ve.Errors&(jwt.ValidationErrorExpired|jwt.ValidationErrorNotValidYet) != 0 {
                // Token is either expired or not active yet
                return nil, "令牌已过期或尚未激活"
            } else {
                // Couldn't handle this token
                return nil, "无法处理此令牌"
            }
        } else {
            // Couldn't handle this token
            return nil, "无法处理此令牌"
        }
    }
    if !token.Valid {
        return nil, "令牌无效"
    }
    return token, nil
}

验证通过:

验证失败时:

刚接触go不久,实现有点lou,大神勿喷,如果有错误或者有其他优化方案,希望大神们给小弟补补课,我很乐意接受批评,互相学习。