使用 Go HTTP 框架 Hertz 进行 JWT 认证
作者:互联网
Demo 介绍
- 使用命令行工具
hz
生成代码 - 使用
JWT
扩展完成登陆认证和授权访问 - 使用
Gorm
访问MySQL
数据库
Demo 下载
git clone https://github.com/cloudwego/hertz-examples.git
cd bizdemo/hertz_jwt
Demo 结构
hertz_jwt
├── Makefile # 使用 hz 命令行工具生成 hertz 脚手架代码
├── biz
│ ├── dal
│ │ ├── init.go
│ │ └── mysql
│ │ ├── init.go # 初始化数据库连接
│ │ └── user.go # 数据库操作
│ ├── handler
│ │ ├── ping.go
│ │ └── register.go # 用户注册 handler
│ ├── model
│ │ ├── sql
│ │ │ └── user.sql
│ │ └── user.go # 定义数据库模型
│ ├── mw
│ │ └── jwt.go # 初始化 hertz-jwt 中间件
│ ├── router
│ │ └── register.go
│ └── utils
│ └── md5.go # md5 加密
├── docker-compose.yml # mysql 容器环境支持
├── go.mod
├── go.sum
├── main.go # hertz 服务入口
├── readme.md
├── router.go # 路由注册
└── router_gen.go
Demo 分析
下方是这个 demo 的接口列表。
// customizeRegister registers customize routers.
func customizedRegister(r *server.Hertz) {
r.POST("/register", handler.Register)
r.POST("/login", mw.JwtMiddleware.LoginHandler)
auth := r.Group("/auth", mw.JwtMiddleware.MiddlewareFunc())
auth.GET("/ping", handler.Ping)
}
用户注册
对应 /register
接口,当前 demo 的用户数据通过 gorm 操作 mysql 完成持久化,因此在登陆之前,需要对用户进行注册,注册流程为:
- 获取用户名密码和邮箱
- 判断用户是否存在
- 创建用户
用户登陆(认证)
服务器需要在用户第一次登陆的时候,验证用户账号和密码,并签发 jwt token。
JwtMiddleware, err = jwt.New(&jwt.HertzJWTMiddleware{
Key: []byte("secret key"),
Timeout: time.Hour,
MaxRefresh: time.Hour,
Authenticator: func(ctx context.Context, c *app.RequestContext) (interface{}, error) {
var loginStruct struct {
Account string `form:"account" json:"account" query:"account" vd:"(len($) > 0 && len($) < 30); msg:'Illegal format'"`
Password string `form:"password" json:"password" query:"password" vd:"(len($) > 0 && len($) < 30); msg:'Illegal format'"`
}
if err := c.BindAndValidate(&loginStruct); err != nil {
return nil, err
}
users, err := mysql.CheckUser(loginStruct.Account, utils2.MD5(loginStruct.Password))
if err != nil {
return nil, err
}
if len(users) == 0 {
return nil, errors.New("user already exists or wrong password")
}
return users[0], nil
},
PayloadFunc: func(data interface{}) jwt.MapClaims {
if v, ok := data.(*model.User); ok {
return jwt.MapClaims{
jwt.IdentityKey: v,
}
}
return jwt.MapClaims{}
},
})
- Authenticator:用于设置登录时认证用户信息的函数,demo 当中定义了一个
loginStruct
结构接收用户登陆信息,并进行认证有效性。这个函数的返回值users[0]
将为后续生成 jwt token 提供 payload 数据源。 - PayloadFunc:它的入参就是
Authenticator
的返回值,此时负责解析users[0]
,并将用户名注入 token 的 payload 部分。
- Key:指定了用于加密 jwt token 的密钥为
"secret key"
。 - Timeout:指定了 token 有效期为一个小时。
- MaxRefresh:用于设置最大 token 刷新时间,允许客户端在
TokenTime
+MaxRefresh
内刷新 token 的有效时间,追加一个Timeout
的时长。
Token 的返回
JwtMiddleware, err = jwt.New(&jwt.HertzJWTMiddleware{
LoginResponse: func(ctx context.Context, c *app.RequestContext, code int, token string, expire time.Time) {
c.JSON(http.StatusOK, utils.H{
"code": code,
"token": token,
"expire": expire.Format(time.RFC3339),
"message": "success",
})
},
})
- LoginResponse:在登陆成功之后,jwt token 信息会随响应返回,你可以自定义这部分的具体内容,但注意不要改动函数签名,因为它与
LoginHandler
是强绑定的。
Token 的校验
访问配置了 jwt 中间件的路由时,会经过 jwt token 的校验流程。