Hertz-jwt认证 | 青训营笔记

356 阅读3分钟

这是我参与「第五届青训营 」伴学笔记创作活动的第 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