使用 GoFrame 框架 JWT 方式验证用户信息
说明
- 框架使用 GoFrame 国产框架
强哥
开发 - jwt使用 https://github.com/gogf/gf-jwt
鹏哥
编写的 - 权限使用
casbin
插件 地址
使用说明
官网的 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使用就已经完成,希望对大家有帮助
截图说明
请求登录成功
拿登录成功的token 刷新 token
未使用token请求刷新接口
后面会出原生的jwt实现方式,使用插件比较快速,
以后也会出casbin
权限认证
源文件地址 https://github.com/zhouyaozhouyao/goframe-...
本作品采用《CC 协议》,转载必须注明作者和本文链接
推荐文章: