什么是 JWT
JWT
is short for JSON Web Token
JSON Web Token (JWT) is an open standard (RFC 7519) that defines a compact and self-contained way for securely transmitting information between parties as a JSON object. This information can be verified and trusted because it is digitally signed. JWTs can be signed using a secret (with the HMAC algorithm) or a public/private key pair using RSA or ECDSA.
可见,JWT
主要是用来在不同组间安全传输 JSON 信息。
自我包含,可被信任。
JWT 结构
三部分组成,由.
分割
- 头部(加密信息)
- 负荷(需要传输的 JSON)
- 签名(验证使用)
最终的结果类似于:xxxxx.yyyyy.zzzzz
头
{
"alg": "HS256",
"typ": "JWT"
}
由加密算法和 JWT 标识组成
负荷
这里就是你要传输的json
结构体了,不建议传输私密信息,JWT 不是干这个的。
签名
根据上面两个内容来生产一个签名,签名用来验证消息发送者的真伪。
HMACSHA256(base64UrlEncode(header) + "." + base64UrlEncode(payload), secret);
上面的就是签名,其中secret
是私钥,需要自己保管好。
最后讲上面三个内容按照先进行base64
之后再按照aa.bb.cc
组合在一起就是一个 JWT 了,看上去还是挺简单的。
需要记住的是,payload
只是经过简单base64
因此不能传输私密内容。
Goland 的坑
github
上面的 jwt 包,一般都是使用map[string]interface{}
来装载任意json
结构体。
在进行payload
这一步的时候,会用到json.Marshal()
方法来产生json
字符串。
由于使用到了map
,总所周知,map
的遍历是无序的,因此同一个json
结构,可能会参数不同的JWT
出来,如果服务器对此收到的payload
字段位置没有要求,这样做是没有问题。但是如果,有要求的话,就会产生问题。
为此,解决方案是放弃map
方案,使用struct
结构体,这样的不便之处就是,必须为每一个json
结构定义一个struct
,按照需要的字段定义即可,这样序列化结构体的时候就可以保留字段的信息。
虽然会有点不方便,不过确实是一个很不错的解决方案。
附,简易的jwt
实现
package jwt
import (
"crypto/hmac"
"crypto/sha256"
"encoding/base64"
"encoding/json"
)
type HS256 struct {
Claim interface{}
Key string
}
type header struct {
Alg string `json:"alg"`
Typ string `json:"typ"`
}
func (h HS256) header() *header {
return &header{
Typ: "JWT",
Alg: "HS256",
}
}
func (h HS256) Encode() (string, error) {
header, _ := json.Marshal(h.header())
jsonHeader := base64.RawURLEncoding.EncodeToString(header)
claim, err := json.Marshal(h.Claim)
if err != nil {
return "", err
}
jsonClaim := base64.RawURLEncoding.EncodeToString(claim)
unsigned := jsonHeader + "." + jsonClaim
hh := hmac.New(sha256.New, []byte(h.Key))
if _, err := hh.Write([]byte(unsigned)); err != nil {
return "", err
}
signed := base64.RawURLEncoding.EncodeToString(hh.Sum(nil))
return jsonHeader + "." + jsonClaim + "." + signed, nil
}
使用示例
func main(){
j := jwt.HS256{
Claim: struct {
P string `json:"p"`
T int64 `json:"t"`
B string `json:"b"`
W int `json:"w"`
Iat int64 `json:"iat"`
}{
P: "11",
T: 1583075639000,
B: "12054",
W: 1000,
Iat: 1583075639,
},
Key: jwtKey,
}
fmt.Println(j.Encode())
}