JWT组成
JWT 全称 JSON Web Tokens ,是一种规范化的 token。可以理解为对 token 这一技术提出一套规范,是在 RFC 7519 中提出的
JWT由三部分组成,头部、载荷与签名,中间用.
分隔,例如:xxxx.yyyy.zzzz
我们使用demo使用章节中登录的token作为解读
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJleHAiOjE2MTIwMTMwMTMsImlhdCI6MTYxMjAxMjcxMywiaWQiOjEsInVzZXJuYW1lIjoiYWRtaW4ifQ.avsmOHGyHWIovihtoW7bjuQbXs81v1nKiZ9iYMZYwOs
头部(Header)
头部通常由两部分组成:令牌的类型(即 JWT)和正在使用的签名算法(如HS256 RSA等)
{"alg":"HS256","typ":"JWT"}
经过base64编码后得到eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9
$ echo -n '{"alg":"HS256","typ":"JWT"}'| base64 eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9 $ echo -n 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9'| base64 -d {"alg":"HS256","typ":"JWT"}
载荷(Payload)
载荷中放置了 token 的一些基本信息,以帮助接受它的服务器来理解这个 token。同时还可以包含一些自定义的信息,用户信息交换
载荷的属性也分三类:
- 预定义(Registered)
- 公有(public)
- 私有(private)
预定义
{ "sub": "1", "iss": "http://localhost:8000/auth/login", "iat": 1451888119, "exp": 1454516119, "nbf": 1451888119, "jti": "37c107e4609ddbcc9c096ea5ee76c667", "aud": "dev" }
这里面的前 7 个字段都是由官方所定义的,也就是预定义(Registered claims)的,并不都是必需的
- iss (issuer):签发人
- sub (subject):主题
- aud (audience):受众
- exp (expiration time):过期时间
- nbf (Not Before):生效时间,在此之前是无效的
- iat (Issued At):签发时间
- jti (JWT ID):编号
在gf-jwt中,仅使用了exp
和iat
公有载荷
在使用 JWT 时可以额外定义的载荷。为了避免冲突,应该使用 IANA JSON Web Token Registry 中定义好的,或者给额外载荷加上类似命名空间的唯一标识
私有载荷
在信息交互的双方之间约定好的,既不是预定义载荷也不是公有载荷的一类载荷。这一类载荷可能会发生冲突,所以应该谨慎使用
而在demo中,私有载荷为{"id": 1, "username": "admin"}
将预定义,公有载荷,私有载荷组合起来{"exp":1612013013,"iat":1612012713,"id":1,"username":"admin"}
进行base64编码得到eyJleHAiOjE2MTIwMTMwMTMsImlhdCI6MTYxMjAxMjcxMywiaWQiOjEsInVzZXJuYW1lIjoiYWRtaW4ifQ
// ==经过strings.TrimRight处理不显示 $ echo -n '{"exp":1612013013,"iat":1612012713,"id":1,"username":"admin"}' | base64 eyJleHAiOjE2MTIwMTMwMTMsImlhdCI6MTYxMjAxMjcxMywiaWQiOjEsInVzZXJuYW1lIjoiYWRtaW4ifQ== $ echo -n 'eyJleHAiOjE2MTIwMTMwMTMsImlhdCI6MTYxMjAxMjcxMywiaWQiOjEsInVzZXJuYW1lIjoiYWRtaW4ifQ' | base64 -d {"exp":1612013013,"iat":1612012713,"id":1,"username":"admin"}
签名(Signature)
签名时需要用到前面编码过的两个字符串,如果以 HS256 加密为例,就如下:
HMACSHA256( base64UrlEncode(header) + "." + base64UrlEncode(payload), secret )
secret
就是配置说明中的Key
,因此密钥需要妥善保管,不能泄露
加密后再进行 base64url 编码最后得到的字符串就是 token 的第三部分 zzzzz
组合便可以得到 token:xxxxx.yyyyy.zzzzz
func Hs256(message string, secret string) string { hasher := hmac.New(sha256.New, []byte(secret)) hasher.Write([]byte(message)) sign := strings.TrimRight(base64.URLEncoding.EncodeToString(hasher.Sum(nil)), "=") return sign } // main.go func main() { sign := Hs256("eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJleHAiOjE2MTIwMTMwMTMsImlhdCI6MTYxMjAxMjcxMywiaWQiOjEsInVzZXJuYW1lIjoiYWRtaW4ifQ", "secret key") fmt.Println(sign) // avsmOHGyHWIovihtoW7bjuQbXs81v1nKiZ9iYMZYwOs }
签名的作用:保证 JWT 没有被篡改过,原理如下:
HMAC 算法是不可逆算法,类似 MD5 和 hash ,但多一个密钥,密钥(即上面的 secret)由服务端持有,客户端把 token 发给服务端后,服务端可以把其中的头部和载荷再加上事先共享的 secret 再进行一次 HMAC 加密,得到的结果和 token 的第三段进行对比,如果一样则表明数据没有被篡改。
2 Comments
王中阳Go
GitHub地址:https://github.com/gogf/gf-jwt/
王中阳Go
视频版教程在这里:https://www.bilibili.com/video/BV163411f7Y8/