什么是JWT
Json web token (JWT), 是为了在网络应用环境间传递声明而执行的一种基于JSON的开放标准。该token被设计为紧凑且安全的,特别适用于分布式站点的单点登录(SSO)场景。JWT的声明一般被用来在身份提供者和服务提供者间传递被认证的用户身份信息,以便于从资源服务器获取资源,也可以增加一些额外的其它业务逻辑所必须的声明信息,该token也可直接被用于认证,也可被加密。
JWT的构成
第一部分我们称它为头部(header),第二部分我们称其为载荷(payload, 类似于飞机上承载的物品),第三部分是签证(signature)。
Header
jwt的头部承载两部分信息:
- 声明类型,这里是jwt
- 声明加密的算法 通常直接使用 HMAC SHA256
完整的头部就像下面这样的JSON:
{
"alg": "HS256",
"typ": "JWT"
}
然后,用Base64对这个JSON编码就得到JWT的第一部分。
Payload
载荷就是存放有效信息的地方。这个名字像是特指飞机上承载的货品,这些有效信息包含三个部分
- 标准中注册的声明
- 公共的声明
- 私有的声明
标准中注册的声明 (建议但不强制使用) :
- iss: jwt签发者
- sub: jwt所面向的用户
- aud: 接收jwt的一方
- exp: jwt的过期时间,这个过期时间必须要大于签发时间
- nbf: 定义在什么时间之前,该jwt都是不可用的.
- iat: jwt的签发时间
- jti: jwt的唯一身份标识,主要用来作为一次性token,从而回避重放攻击。
公共的声明 :公共的声明可以添加任何的信息,一般添加用户的相关信息或其他业务需要的必要信息.但不建议添加敏感信息,因为该部分在客户端可解密。
私有的声明 :私有声明是提供者和消费者所共同定义的声明,一般不建议存放敏感信息,因为base64是对称解密的,意味着该部分信息可以归类为明文信息。
{
"user": 'admin',
"exp":1608277183,
"iss":"bigfool.net"
}
对payload进行Base64编码就得到JWT的第二部分;不要在JWT的payload或header中放置敏感信息,除非它们是加密的。
Signature
jwt的第三部分是一个签证信息,这个签证信息由三部分组成:
- header (base64后的)
- payload (base64后的)
- secret(自定义私有key)
这个部分需要base64加密后的header和base64加密后的payload使用.连接组成的字符串,然后通过header中声明的加密方式进行加盐secret组合加密,然后就构成了jwt的第三部分。
最后三者之间用 . 拼接起来就是token = header.playload.signature。
Golang应用
使用github.com/dgrijalva/jwt-go包来生成token和解析token。
安装:
go get github.com/dgrijalva/jwt-go
具体使用:
package utils
import (
jwt "github.com/dgrijalva/jwt-go"
"nuxt-blog-api/configs"
"strings"
"time"
)
// 获取配置文件自定义私有签名key
var jwtSecret = []byte(configs.Configs.App.JwtSecret)
type Claims struct {
UserName string `json:"username"`
jwt.StandardClaims
}
type JwtDo struct {
}
// 生成token
func (JwtDo) GenerateToken(userName string) (string, error) {
// 过期时间设置8小时
nowTime := time.Now()
expireTime := nowTime.Add(8 * time.Hour)
claims := Claims{
userName,
jwt.StandardClaims {
ExpiresAt : expireTime.Unix(),
Issuer : "bigfool.cn",
},
}
tokenClaims := jwt.NewWithClaims(jwt.SigningMethodHS256, claims)
token, err := tokenClaims.SignedString(jwtSecret)
return token, err
}
// 解析token
func (JwtDo) ParseToken(token string) (*Claims, error) {
if strings.Contains(token,"Bearer ") {
token = strings.Replace(token,"Bearer ","",1)
}
tokenClaims, err := jwt.ParseWithClaims(token, &Claims{}, func(token *jwt.Token) (interface{}, error) {
return jwtSecret, nil
})
if tokenClaims != nil {
if claims, ok := tokenClaims.Claims.(*Claims); ok && tokenClaims.Valid {
return claims, nil
}
}
return nil, err
}
var Jwt = new(JwtDo)
调用:
// 生成token
token, err := utils.Jwt.GenerateToken("耗子尾汁")
// 解析token
claims, err := utils.Jwt.ParseToken(token)