Json web token (JWT)
eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiYWRtaW4iOnRydWV9.UQmqAUhUrpDVV2ST7mZKyLTomVfg7sYkEjmdDI5XF8Q
三部分构成
第一部头部(header)的作用
声明类型
声明加密的算法 通常直接使用 HMAC SHA256
{'typ': 'JWT','alg': 'HS256'}
将头部进行base64加密
eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9
第二部分载荷(payload)
载荷就是存放有效信息的地方:
标准中注册的声明
iss: jwt签发者sub: jwt所面向的用户aud: 接收jwt的一方exp: jwt的过期时间,这个过期时间必须要大于签发时间nbf: 定义在什么时间之前,该jwt都是不可用的.iat: jwt的签发时间jti: jwt的唯一身份标识,主要用来作为一次性token,从而回避重放攻击公共的声明
公共的声明可以添加任何的信息一般添加用户的相关信息或其他业务需要的必要信息议添加敏感信息,因为该部分在客户端可解密私有的声明
私有声明是提供者和消费者所共同定义的声明,一般不建议存放敏感信息,因为base64是对称解密的,意味着该部分信息可以归类为明文信息
举例:{"sub": "1234567890","name": "John Doe","admin": true}然后将其进行base64加密,得到Jwt的第二部分
eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiYWRtaW4iOnRydWV9signature
第三部分签证(signature)
UQmqAUhUrpDVV2ST7mZKyLTomVfg7sYkEjmdDI5XF8Qheader (base64后的)payload (base64后的)secret需要base64加密后的header和base64加密后的payload使用"."连接组成的字符串(头部在前),然后通过header中声明的加密方式进行加secret组合加密,构成了jwt的第三部分举例secret 需要保存在服务端用来生成token和验证
签名的目的
对头部以及载荷内容进行签名加密算法对于不同的输入产生的输出总是不一样对于两个不同的输入,产生同样的输出的概率极其地小JWT的头部中已经用alg字段指明加密算法服务器应用对头部和载荷再次以同样方法签名之后发现,自己计算出来的签名和接受到的签名不一样,说明这个Token的内容被别人动过的,我们应该拒绝这个Token,返回一个HTTP 401 Unauthorized响应在JWT中,不应该在载荷里面加入任何敏感的数据,比如用户的密码如何应用
一般是在请求头里加入Authorization,并加上Bearer标注
fetch('api/user/1', { headers: {'Authorization': 'Bearer ' + token }})服务端会验证token
如果验证通过,就会返回相应的资源
Golang 客户端实现
调用包 https://github.com/tutengdihuang/jwt
单例模式var jwtIns *JwtAuth
type JwtAuth struct {
Lock *sync.Mutex
algorithm *jwt.Algorithm
claimsMap map[string]*jwt.Claims //key: userID
}
func GetJwtAuth() *JwtAuth {
if jwtIns == nil {
once := sync.Once{}
once.Do(
func() {
if jwtIns == nil {
jwtIns = new(JwtAuth)
jwtIns.Lock = new(sync.Mutex)
jwtIns.claimsMap = make(map[string]*jwt.Claims)
secret := config_center.GetViperConf().Sub("jwt").GetString("secret")
algorithm := jwt.HmacSha256(secret)
jwtIns.algorithm = &algorithm
}
})
}
return jwtIns
}
get token
var jwtClaimUserKey = "username"
var jwtClaimUserIdKey = "id"
func (this *JwtAuth) GetToken(userName string, id int) (token string, err error) {
claim := jwt.NewClaim()
claim.Set(jwtClaimUserKey, userName)
claim.Set(jwtClaimUserIdKey, strconv.Itoa(id))
//claim.SetTime("expire", time.Now().Add(30*time.Minute))
idstr := strconv.Itoa(id)
this.Lock.Lock()
defer this.Lock.Unlock()
this.claimsMap[idstr] = claim
token, err = this.algorithm.Encode(claim)
return
}
validate check
func (this *JwtAuth) Validate(idstr, token string) bool {
this.Lock.Lock()
defer this.Lock.Unlock()
if _, ok := this.claimsMap[idstr]; !ok {
return false
}
if this.algorithm.Validate(token) != nil {
return false
}
return true
}
token decode
func (this *JwtAuth) TokenDecode(token string) (string, int, error) {
claim, err := this.algorithm.Decode(token)
if err != nil {
return "", 0, err
}
userName, err := claim.Get(jwtClaimUserKey)
if err != nil {
return "", 0, err
}
idStr, err := claim.Get(jwtClaimUserIdKey)
if err != nil {
return "", 0, err
}
id, _ := strconv.Atoi(idStr)
return userName, id, nil
}
token remove
func (this *JwtAuth) TokenRemove(id string) {
this.Lock.Lock()
defer this.Lock.Unlock()
delete(this.claimsMap, id)
}
总结
以上为个人经验,希望能给大家一个参考,也希望大家多多支持。
