这是我参与「第五届青训营 」伴学笔记创作活动的第 15 天
JWT认证
JSON Web Token(JWT)是一个轻量级的认证规范,允许我们使用JWT在用户和服务器之间传递安全可靠的信息。其本质是一种紧凑的URL安全方法。
Token结构
- Header头部: Token类型,签名算法
- Payload载荷:实体的表述,额外信息
- Signature签名:由Header、Payload、Secret在部分进行签名得到
一个Token形如xxx.yyy.zzz
JWT如何工作
在认证过程中,用户登录成功后,会返回一个jwt token。当用户请求一个被保护的路由或资源时,用户代理应当发送JWT,例如在Header中加上Authorization
Authorization: Bearer <token>
jwt是一种无状态的授权方式,服务端不需要存储用户token相关信息。用户发送JWT token到服务器,服务器可用相应算法解析token,判断token是否有效。
为什么要使用JWT
- CSRF: session基于cookie,如果cookie被截获,用户容易收到跨站请求伪造的攻击。
- 基于session的认证方式开销大,扩展性低。
- 由于json的通用性,JWT天然支持跨语言
- 由于payload的存在,JWT可以在自身存储一些业务逻辑需要的非敏感信息
- 由于JWT构成简单,字节占用小,便于传输
- 由于不需要在服务端保存会话信息,易于扩展
Hertz-jwt
安装hertz jwt拓展
go get github.com/hertz-contrib/jwt
使用
func main() {
Init()
h := server.New(
server.WithHostPorts("0.0.0.0:8080"),
server.WithHandleMethodNotAllowed(true),
)
authMiddleware, err := jwt.New(&jwt.HertzJWTMiddleware{
Key: []byte(constants.SecretKey),
Timeout: time.Hour * 24,
MaxRefresh: time.Hour * 24,
//用户登录, 登录成功返回用户id
Authenticator: func(ctx context.Context, c *app.RequestContext) (interface{}, error) {
//return int64(1), nil
// 获取传入的username和password
var loginValues handler.UserParam
err := c.Bind(&loginValues)
if err != nil {
return int64(0), jwt.ErrMissingLoginValues
}
username := loginValues.Username
password := loginValues.Password
if len(username) == 0 || len(password) == 0 {
return int64(0), jwt.ErrMissingLoginValues
}
// 调用rpc验证登录,获取user id
return rpc.CheckUser(&userapi.CheckUserRequest{
Username: username,
Password: password,
})
},
//向jwt token payload中写入用户id
PayloadFunc: func(data interface{}) jwt.MapClaims {
if v, ok := data.(int64); ok {
return jwt.MapClaims{
constants.IdentityKey: v,
}
}
return jwt.MapClaims{}
},
//登录成功的响应
LoginResponse: func(ctx context.Context, c *app.RequestContext, code int, token string, expire time.Time) {
c.JSON(consts.StatusOK, map[string]interface{}{
"status_code": 0,
"status_msg": "success",
"user_id": 0,
"token": token,
"expire": expire.Format(time.RFC3339),
})
},
//登录失败的响应
Unauthorized: func(ctx context.Context, c *app.RequestContext, code int, message string) {
c.JSON(code, map[string]interface{}{
"status_code": 1,
"status_msg": "authorization failed",
})
},
//刷新token的响应
RefreshResponse: func(ctx context.Context, c *app.RequestContext, code int, token string, expire time.Time) {
c.JSON(http.StatusOK, map[string]interface{}{
"status_code": 0,
"status_msg": "success",
"token": token,
"expire": expire.Format(time.RFC3339),
})
},
TokenLookup: "header: Authorization, query: token, cookie: jwt",
TokenHeadName: "Bearer",
TimeFunc: time.Now,
IdentityKey: constants.IdentityKey,
})
if err != nil {
log.Fatal(err)
}
h.Use(authMiddleware.MiddlewareFunc())
h.Spin()
}
使用jwt扩展时,要为/login接口绑定认证逻辑authMiddleware.LoginHandler, 为/refresh接口绑定刷新Token逻辑authMiddleware.RefreshHandler。还要以中间件的方式,为需要授权访问的路由组注入授权逻辑authMiddleware.MiddlewareFunc()
配置
- Key: Token的签名密钥(必要配置)
- KeyFunc:设置获取签名密钥的回调函数,设置后token解析时将从KeyFunc获取jwt签名密钥
- Timeout: token过期时间
- MaxRefresh:最大token刷新时间
- Authenticator:登录时认证用户信息的函数
- Authorizator:设置授权己认证用户路由访问权限的函数
- PayloadFunc:登录成功后向token中添加自定义负载的函数
- Unauthorized: 设置jwt验证失败的响应函数
- LoginResponse:登录成功的响应函数
- LogoutResponse:登出的响应函数
- RefreshResponse:刷新token的响应函数
- IdendityKey:用于检索身份信息的Key,默认为identity
- TokenLookup:用于设置获取token的源,可以选择header、query、cookie、param、form
- TokenHeaderName:设置从header中获取token时的前缀,默认为Bearer