使用 GoFrame 框架 JWT 方式验证用户信息

说明

使用说明

官网的 gf-demos 已经包含了基本的示例操作,但是现在都属于前后端分离开发模式,特对用户信息无状态保存做了一翻更改,使用jwt生成的token来做用户状态识别,接下来进入正题

一、安装框架与gf-jwt插件

先安装框架提供的gf-cli命令行工具 官网地址 执行下面命令

1、  Linux 安装
    wget https://goframe.org/cli/linux_amd64/gf && chmod +x gf && sudo ./gf install

2、Mac 安装
    wget https://goframe.org/cli/darwin_amd64/gf && chmod +x gf && ./gf install

更多方式安装见官网

1、我们使用gf-cli工具来生成我们的项目脚手架

$ cd 你要存放项目的目录  例:/home/www/goframe
$ gf init <应用名称> 例 goframe  回车就行
$ gf get  github.com/gogf/gf-jwt  安装jwt包

2、我们会在当前目录生成以下结构的目录

/
├── app 
│   ├── api
│   ├── model
│   └── service
├── boot
├── config
├── docker
├── document
├── i18n
├── library
├── public
├── router
├── template
├── vendor
├── Dockerfile
├── go.mod
└── main.go

详细的目录说明见官网 传送门

2、建立路由与jwt相关文件

1、打开 router/router.go文件

package router

import (
    "gadmin/app/api/admin/login"
    "gadmin/app/api/admin/rbac"
    "gadmin/app/api/admin/user"
    "gadmin/app/middleware/token"

    "github.com/gogf/gf/frame/g"
    "github.com/gogf/gf/net/ghttp"
)

    func init() {
        s := g.Server()
        s.Group("/v1", func(group *ghttp.RouterGroup) {
            // 登录逻辑走login包
           group.POST("/admin/loginSubmit", login.GfJWTMiddleware.LoginHandler)
           group.Group("/admin", func(group *ghttp.RouterGroup) {
               // 中间件检测token是否有效
              group.Middleware(token.Validator)
               // 刷新token令牌
              group.GET("/refresh", login.GfJWTMiddleware.RefreshHandler)
            })
        })
    }
...

2、打开 app/api/admin/login 文件

package login

import (
    "errors"
    "gadmin/app/model/users"
    "gadmin/library/base"
    "gadmin/library/helper"
    "gadmin/library/input"
    "gadmin/library/redis"
    "time"

    "github.com/gogf/gf/util/gconv"

    "github.com/gogf/gf/crypto/gmd5"
    "github.com/gogf/gf/frame/g"

    "github.com/gogf/gf/util/gvalid"

    "github.com/gogf/gf/net/ghttp"

    jwt "github.com/gogf/gf-jwt"
    "github.com/gogf/gf/os/glog"
)

var (
    GfJWTMiddleware *jwt.GfJWTMiddleware // 声明jwt包的全局变量
)

type SignRequest struct {
    Username string `v:required#账号不能为空 json:"username"`
    Password string `v:required#密码不能为空 json:"password"`
}

func init() {
    authMiddleWare, err := jwt.New(&jwt.GfJWTMiddleware{
        Realm:           "test zhou",           // 用于展示中间件的名称
        Key:             []byte("secret key"),  // 密钥
        Timeout:         time.Minute * 5,       // token过期时间
        MaxRefresh:      time.Minute * 5,
        IdentityKey:     "id",                  // 身份验证的key值
        TokenLookup:     "header: Authorization, query: token, cookie: jwt",    // token检索模式,用于提取token-> Authorization
        TokenHeadName:   "Bearer",              // token在请求头时的名称,默认值为Bearer
                                                // 客户端在header中传入Authorization 对一个值是Bearer + 空格 + token
        TimeFunc:        time.Now,              // 测试或服务器在其他时区可设置该属性
        Authenticator:   Authenticator,         // 根据登录信息对用户进行身份验证的回调函数
        LoginResponse:   LoginResponse,         // 完成登录后返回的信息,用户可自定义返回数据,默认返回
        RefreshResponse: auth.RefreshResponse,  // 刷新token后返回的信息,用户可自定义返回数据,默认返回
        Unauthorized:    auth.Unauthorized,     // 处理不进行授权的逻辑
        IdentityHandler: auth.IdentityHandler,  // 解析并设置用户身份信息
        PayloadFunc:     auth.PayloadFunc,      // 登录期间的回调的函数
    })

    if err != nil {
        glog.Error("JWT Error:" + err.Error())
    }

    GfJWTMiddleware = authMiddleWare
}

// Authenticator 检测身份信息是否正常
func Authenticator(r *ghttp.Request) (interface{}, error) {
    var req *SignRequest
    // 接收参数
    input.JSONToStruct(r, &req)

    // 校验数据参数
    if err := gvalid.CheckStruct(req, nil); err != nil {
        base.FailParam(r, err.String())
    }

    // 查询数据
    res := users.GetOne(g.Map{"username": req.Username})

    if res.Id <= 0 {
        return nil, errors.New("用户名或密码错误")
    }

    reqPwd, errPwd := gmd5.Encrypt(req.Password + res.Salt)
    if errPwd != nil {
        glog.Error("md5加密异常", errPwd)
        return nil, errors.New("服务器异常")
    }

    if reqPwd != res.Password {
        return nil, errors.New("用户名或密码错误~")
    }

    // 设置参数保存到请求中
    r.SetParam("uuid", res.Uuid)

    return g.Map{"username": res.Username, "uuid": res.Uuid}, nil
}

// PostLogin 返回对应的用户信息
func PostLogin(r *ghttp.Request, code int, token string, expire time.Time) {
    j, _ := r.GetJson()
    // 格式化时间
    t := helper.TimeToString(expire)

    // 获取配置文件中的redis前缀
    var loginPrefix = g.Cfg("redis").Get("APP.LOGIN_PREFIX")
    redis.Set(gconv.String(loginPrefix)+gconv.String(r.GetParam("uuid")), token)

    base.Success(r, g.Map{
        "username": j.GetString("username"),
        "token":    token,
        "expire":   t,
    })
}

// RefreshResponse 刷新token信息
func RefreshResponse(r *ghttp.Request, code int, token string, expire time.Time) {
    base.Success(r, g.Map{"token": token, "expire": helper.TimeToString(expire)})
}

// Unauthorized 返回验证错误信息
func Unauthorized(r *ghttp.Request, code int, message string) {
    // TODO 英文提示可在此处转换为中文,暂没找到好的方式后续单做一个配置文件
    base.FailParam(r, message)
}

// IdentityHandler sets the identity for JWT.
func IdentityHandler(r *ghttp.Request) interface{} {
    claims := jwt.ExtractClaims(r)
    return claims["id"]
}

// PayloadFunc 给token中添加其它字段的数据
func PayloadFunc(data interface{}) jwt.MapClaims {
    claims := jwt.MapClaims{}
    params := data.(map[string]interface{})
    if len(params) > 0 {
        for k, v := range params {
            claims[k] = v
        }
    }
    return claims
}

3、 打开 app/middleware/token 检测jwt是否有效的中间件

package token

import (
    "gadmin/app/api/admin/login"
    "gadmin/library/base"
    "gadmin/library/redis"

    "github.com/gogf/gf/frame/g"
    "github.com/gogf/gf/net/ghttp"
    "github.com/gogf/gf/util/gconv"
)

// Validator 验证token有效性
func Validator(r *ghttp.Request) {
    login.GfJWTMiddleware.MiddlewareFunc()(r)
    // 解析token
    parseToken, _ := login.GfJWTMiddleware.ParseToken(r)
    var token = parseToken.Raw
    // 解析otken中保存的内容
    var claims = gconv.Map(parseToken.Claims)
    // 登录时存了一份到redis中  验证时需要在校验一次
    if !GetRedisToken(gconv.String(claims["uuid"]), token) {
        base.Fail(r, "重新登录")
    }
    r.Middleware.Next()
}

// GetRedisToken 获取缓存中的token与客户端token对比
func GetRedisToken(uuid string, oldToken string) bool {
    redisPrefix := gconv.String(g.Cfg("redis").Get("APP.LOGIN_PREFIX"))
    key := redisPrefix + uuid
    if redis.Get(key) != oldToken {
        return false
    }
    return gconv.Bool(true)
}

到此 jwt使用就已经完成,希望对大家有帮助

截图说明

请求登录成功
使用GoFrame框架 JWT方式验证用户信息

拿登录成功的token 刷新 token
使用GoFrame框架 JWT方式验证用户信息

未使用token请求刷新接口
使用GoFrame框架 JWT方式验证用户信息

后面会出原生的jwt实现方式,使用插件比较快速,

以后也会出casbin权限认证

源文件地址 https://github.com/zhouyaozhouyao/goframe-...

本作品采用《CC 协议》,转载必须注明作者和本文链接
994914376
讨论数量: 0
(= ̄ω ̄=)··· 暂无内容!

讨论应以学习和精进为目的。请勿发布不友善或者负能量的内容,与人为善,比聪明更重要!