JWT 介绍

JWT 即 JSON Web Token,是用 JSON 形式安全传输信息的方法。

我们可以在 jwt.io 上在线解析 token,可以清楚看懂 JWT 的数据结构。

对 JWT 解码,可以得到以下内容:

  • Header:TOKEN 的类型,就是JWT,签名的算法,如 HMAC、 SHA256
  • Payload:携带的信息,比如用户名、过期时间等,一般叫做 Claim
  • Signature:签名,是由header、payload 和你自己维护的一个 secret 经过加密得来的。

JWT 可以设置过期时间,它的应用主要有:

  • Access Token:添加到 HTTP 请求的 header 中,进行用户认证。加上过期时间可以让 token 被恶意截获后,黑客只有短暂的时间攻击。

  • Refresh Token:用来给客户端申请新的 Access Token 或者 Refresh Token。比 Access Token 有更长过期时间。

HttpOnly

Go语言中 JWT 的生成、验证

使用 jwt-go 库生成 JWT。

import github.com/dgrijalva/jwt-go

func CreateToken(uid, secret string) (string, error) {
  at := jwt.NewWithClaims(jwt.SigningMethodHS256, jwt.MapClaims{
	"uid":  uid,
	"exp":  time.Now().Add(time.Minute * 15).Unix(),
  })
  token, err := at.SignedString([]byte(secret))
  if err != nil {
     return "", err
  }
  return token, nil
}

通常客户端传输 JWT 是通过 header 中的 Authorization 字段,好处是避免了 CORS攻击。并且使用 Bearer 模式。也就是:

Authorization: Bear eyJhbGciO...

在 grpc 中,可以将 JWT 放在 metadata 中:

ctx = metadata.NewOutgoingContext(ctx, metadata.Pairs("authorization", "Bearer "+token))
res, _ := client.Call(ctx, ..)

在服务端的验证:

// 忽略 ok为假的情况
incomingContext, ok := metadata.FromIncomingContext(ctx)
value, ok := incomingContext["authorization"]
uid, err := ParseToken(value[0])

解析也需要用 jwt-go 库的函数。

// token: "eyJhbGciO..."
func ParseToken(token string, secret string) (string, error) {
	claim, err := jwt.Parse(token, func(token *jwt.Token) (interface{}, error) {
		return []byte(secret), nil
	})
	if err != nil {
		return "", err
	}
	if uid, ok := claim.Claims.(jwt.MapClaims)["uid"].(string); ok {
                return uid, nil
        }
        return "", fmt.Errorf("fail parse")
}