golang-simple JWT

极简版本的JWT

阅读具体RFC文档,获取大概内容

https://tools.ietf.org/html/rfc7519

抽取最核心部分

  1. jwt的组成
  2. jwt的组成部分的核心算法
  3. jwt的检验

尝试实现

此版本hardcode了 算法为HS256

package main

import (
"crypto/hmac"
"crypto/sha256"
"encoding/base64"
"encoding/json"
"errors"
"fmt"
"log"
"strings"
)

// Token is the generated token
type Token string

func main() {
// type claims struct {
// Iss string `json:"iss"`
// Exp int64 `json:"exp"`
// Sub string `json:",omitempty"`
// Aud interface{} `json:",omitempty"`
// Nbf int64 `json:",omitempty"`
// Iat int64 `json:",omitempty"`
// Jti string `json:",omitempty"`
// }

// 测试例子claims
newClaims := map[string]interface{}{
"iss": "joe",
"exp": 1300819380,
"http://example.com/is_root1": true,
}
token, err := generateToken(nil, newClaims)
if err != nil {
log.Fatal(err)
}

payload, err := token.parser()
if err != nil {
log.Fatal(err)
}
for k, v := range payload {
fmt.Printf("%s : %v %T \n", k, v, v)
}
}

// 根据header 和 claims生成token
func generateToken(header map[string]string, claims map[string]interface{}) (token Token, err error) {
// 给header默认值如果是nil
var joseHeader = map[string]string{
"typ": "JWT",
"alg": "HS256",
}
if header == nil {
header = joseHeader
}
b, err := json.Marshal(header)
if err != nil {
return
}
c, err := json.Marshal(claims)
if err != nil {
return
}
// 生成第三部分
hmacV := hmac.New(sha256.New, []byte(base64.URLEncoding.EncodeToString(b)+"."+base64.URLEncoding.EncodeToString(c)))
// 第一部分.第二部分.第三部分
token = Token(base64.URLEncoding.EncodeToString(b) + "." + base64.URLEncoding.EncodeToString(c) + "." + base64.URLEncoding.EncodeToString(hmacV.Sum(nil)))
return
}

// token 解析
func (token Token) parser() (body map[string]interface{}, err error) {
arr := strings.Split(string(token), ".")

// 至少要有一个 .
if len(arr) < 2 {
err = errors.New("at least contain one '.'")
return
}
// 验证是否是正常的url encode string,并且能够正常解析为json
_, err = validate(arr[0])
if err != nil {
return
}
payload, err := validate(arr[1])
if err != nil {
return
}
// 根据前两个部分,能和第三部分签名匹配
hmacV := hmac.New(sha256.New, []byte(arr[0]+"."+arr[1]))
if !hmac.Equal(hmacV.Sum(nil), []byte(arr[2])) {
err = errors.New("hmac signature is not matched")
return
}
// 把payload部分解析出来
err = json.Unmarshal(payload, &body)
if err != nil {
return
}
return
}

func validate(str string) (payload []byte, err error) {
encodeStr, err := base64.URLEncoding.DecodeString(str)
if err != nil {
return
}
if !json.Valid(encodeStr) {
err = errors.New("can not convert to json")
return
}
return base64.URLEncoding.DecodeString(str)
}