JWT认证初窥|青训营

102 阅读4分钟

JWT简介

JWT全称为Jason Web Token,是一种开放标准(RFC 7519),它定义了一种紧凑且自包含的方式,用于在各方之间作为 JSON 对象安全地传输信息。

JWT主要组成

JWT 主要由三部分组成:

  • Header - 包含声明的类型(通常是JWT)以及签名所用的算法(如HMAC SHA256或RSA)

  • Payload - 包含有效、公共和私有声明。声明是关于实体(通常是用户)和其他数据的陈述。有三种类型的声明:

    • 预定义声明 - 一些预定义的声明,如iss(发行者), exp(过期时间)等
    • 公共声明 - 可以添加一些公共的自定义声明
    • 私有声明 - 一些私有的自定义声明
  • Signature - 对前两部分的签名,防止数据篡改。它通过header指定的算法和秘钥生成。

JWT的工作流程是

  1. 客户端请求服务器,服务器生成一个JWT并返回给客户端。
  2. 客户端将JWT存储(通常存储在localStorage或cookie中)并在每次请求时发送给服务器。
  3. 服务器验证JWT的签名是否有效。如果有效,就处理请求。

Hertz中JWT中间件用法

代码实例:

var (
   JwtMiddleware *jwt.HertzJWTMiddleware
   identity      = "user_id"
)

func Init() {
   JwtMiddleware, _ = jwt.New(&jwt.HertzJWTMiddleware{
   	Key:         []byte("tiktok secret key"),
   	TokenLookup: "query:token,form:token",
   	Timeout:     24 * time.Hour,
   	IdentityKey: identity,
   	// Verify password at login
   	Authenticator: func(ctx context.Context, c *app.RequestContext) (interface{}, error) {
   		var loginRequest user.DouyinUserLoginRequest
   		if err := c.BindAndValidate(&loginRequest); err != nil {
   			return nil, err
   		}
   		user, err := db.QueryUser(loginRequest.Username)
   		if ok := utils.VerifyPassword(loginRequest.Password, user.Password); !ok {
   			err = errno.PasswordIsNotVerified
   			return nil, err
   		}
   		if err != nil {
   			return nil, err
   		}
   		c.Set("user_id", user.ID)
   		return user.ID, nil
   	},
   	// Set the payload in the token
   	PayloadFunc: func(data interface{}) jwt.MapClaims {
   		if v, ok := data.(int64); ok {
   			return jwt.MapClaims{
   				identity: v,
   			}
   		}
   		return jwt.MapClaims{}
   	},
   	// build login response if verify password successfully
   	LoginResponse: func(ctx context.Context, c *app.RequestContext, code int, token string, expire time.Time) {
   		hlog.CtxInfof(ctx, "Login success ,token is issued clientIP: "+c.ClientIP())
   		c.Set("token", token)
   	},
   	// Verify token and get the id of logged-in user
   	Authorizator: func(data interface{}, ctx context.Context, c *app.RequestContext) bool {
   		if v, ok := data.(float64); ok {
   			current_user_id := int64(v)
   			c.Set("current_user_id", current_user_id)
   			hlog.CtxInfof(ctx, "Token is verified clientIP: "+c.ClientIP())
   			return true
   		}
   		return false
   	},
   	// Validation failed, build the message
   	Unauthorized: func(ctx context.Context, c *app.RequestContext, code int, message string) {
   		c.JSON(consts.StatusOK, user.DouyinUserLoginResponse{
   			StatusCode: errno.AuthorizationFailedErrCode,
   			StatusMsg:  message,
   		})
   	},
   	HTTPStatusMessageFunc: func(e error, ctx context.Context, c *app.RequestContext) string {
   		resp := utils.BuildBaseResp(e)
   		return resp.StatusMsg
   	},
   })
}

参数介绍: 详细介绍点击此处

参数介绍
Key用于设置签名密钥(必要配置)
TokenLookup用于设置 token 的获取源,可以选择 headerquerycookieparamform,默认为 header:Authorization
Timeout用于设置 token 过期时间,默认为一小时
IdentityKey用于设置检索身份的键,默认为 identity
Authenticator用于设置登录时认证用户信息的函数(必要配置)
PayloadFunc用于设置登陆成功后为向 token 中添加自定义负载信息的函数
LoginResponse用于设置登录的响应函数
Authorizator用于设置授权已认证的用户路由访问权限的函数
Unauthorized用于设置 jwt 验证流程失败的响应函数
HTTPStatusMessageFunc用于设置 jwt 校验流程发生错误时响应所包含的错误信息

JWT的特点

  • 自包含:JWT包含了用户所需要的所有信息,避免多次查询数据库
  • 更少的服务器查询:可以在JWT的payload部分包含用户权限数据,减少对服务器的查询
  • 跨语言支持:JWT是JSON格式,任何编程语言都可以方便解析
  • 更易于应用的单点登录:通过在payload中包含用户角色和权限信息,易于构建单点登录系统
  • 更好的扩展性:可以在不影响现有系统的情况下,给JWT添加新的声明(claims)

使用JWT的原因

  1. 安全:JWT可以确保传输信息的安全性。由于JWT包含了数字签名,所以信息是经过验证的,可以防止数据在传输途中被篡改。
  2. 跨语言支持:JWT使用JSON格式,JSON解析支持各种编程语言,使它在不同语言的系统之间传递变得很容易。
  3. 便于传输:JWT的格式非常轻量和紧凑,可以通过URL、POST参数或者HTTP header非常方便地传输。
  4. 自包含信息:JWT包含了用户身份信息和其他元数据,避免了多次查询数据库。
  5. 无状态:JWT是独立的,包含了所有必要的信息,不需要在服务端保存会话信息。这使得它特别适用于分布式站点的单点登录认证。
  6. 开销小:使用JWT无需大量的服务器开销,可以简化认证流程,降低部署难度。
  7. 更好的扩展性:JWT可以在不影响现有数据的情况下添加新的信息。
  8. 更少查询数据库:JWT可以在自身包含权限和角色信息,避免每次请求都查询数据库。

总之,使用JWT可以简化认证流程,提高安全性,更好支持不同语言之间的交互,这些都是它被广泛使用的主要原因。JWT解决了传统认证方式的痛点,很好地适应了分布式站点的需求。