JWT简介
JWT全称为Jason Web Token,是一种开放标准(RFC 7519),它定义了一种紧凑且自包含的方式,用于在各方之间作为 JSON 对象安全地传输信息。
JWT主要组成
JWT 主要由三部分组成:
-
Header - 包含声明的类型(通常是JWT)以及签名所用的算法(如HMAC SHA256或RSA)
-
Payload - 包含有效、公共和私有声明。声明是关于实体(通常是用户)和其他数据的陈述。有三种类型的声明:
- 预定义声明 - 一些预定义的声明,如iss(发行者), exp(过期时间)等
- 公共声明 - 可以添加一些公共的自定义声明
- 私有声明 - 一些私有的自定义声明
-
Signature - 对前两部分的签名,防止数据篡改。它通过header指定的算法和秘钥生成。
JWT的工作流程是
- 客户端请求服务器,服务器生成一个JWT并返回给客户端。
- 客户端将JWT存储(通常存储在localStorage或cookie中)并在每次请求时发送给服务器。
- 服务器验证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 的获取源,可以选择 header、query、cookie、param、form,默认为 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的原因
- 安全:JWT可以确保传输信息的安全性。由于JWT包含了数字签名,所以信息是经过验证的,可以防止数据在传输途中被篡改。
- 跨语言支持:JWT使用JSON格式,JSON解析支持各种编程语言,使它在不同语言的系统之间传递变得很容易。
- 便于传输:JWT的格式非常轻量和紧凑,可以通过URL、POST参数或者HTTP header非常方便地传输。
- 自包含信息:JWT包含了用户身份信息和其他元数据,避免了多次查询数据库。
- 无状态:JWT是独立的,包含了所有必要的信息,不需要在服务端保存会话信息。这使得它特别适用于分布式站点的单点登录认证。
- 开销小:使用JWT无需大量的服务器开销,可以简化认证流程,降低部署难度。
- 更好的扩展性:JWT可以在不影响现有数据的情况下添加新的信息。
- 更少查询数据库:JWT可以在自身包含权限和角色信息,避免每次请求都查询数据库。
总之,使用JWT可以简化认证流程,提高安全性,更好支持不同语言之间的交互,这些都是它被广泛使用的主要原因。JWT解决了传统认证方式的痛点,很好地适应了分布式站点的需求。