Dreamer2q Blog
见到,不如不见
Dreamer2q

Code is cheap, talk is expensive

64日志

Go JWT 这点事

创建于 2021-02-15 共 874 字,阅读约 3 分钟 更新于 21-02-15 13:45
浏览 16评论 0


什么是 JWT


JWT is short for JSON Web Token


INTRO


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())
}


END