# 6.4 JWT

# 6.4.1 第三方库

# 6.4.2 配置参数

名称 类型 描述
issuer string jwt发行方
validDuration int 过期时间(秒),默认7200s
secretKey string 秘钥KEY
identityKey string 身份认证KEY
locations string token查找位置
store object token存储配置
store.addrs string 连接地址
store.db int 数据库号,默认为0
store.username string 用户名,默认空
store.password string 密码,默认空
store.keyFile string 私钥文件,默认空
store.certFile string 证书文件,默认空
store.caFile string CA证书文件,默认空
store.maxRetries int 网络相关的错误最大重试次数,默认3次
store.poolSize int 最大连接池限制,默认为200个连接
store.minIdleConns int 最小空闲连接数,默认10个连接
store.dialTimeout string 拨号超时时间,支持单位:纳秒(ns)、微秒(us 或 µs)、毫秒(ms)、秒(s)、分(m)、小时(h)、天(d)。默认为5s
store.idleTimeout string 连接最大空闲时间,支持单位:纳秒(ns)、微秒(us 或 µs)、毫秒(ms)、秒(s)、分(m)、小时(h)、天(d)。默认0s
store.readTimeout string 读超时,支持单位:纳秒(ns)、微秒(us 或 µs)、毫秒(ms)、秒(s)、分(m)、小时(h)、天(d)。默认1s
store.writeTimeout string 写超时,支持单位:纳秒(ns)、微秒(us 或 µs)、毫秒(ms)、秒(s)、分(m)、小时(h)、天(d)。默认1s

# 6.4.3 示例代码

以下完整示例详见:third-jwt-example (opens new window)

  1. 组件封装

文件位置:third-jwt-example/jwt/jwt.go (opens new window)

package jwt

import (
	"fmt"

	"github.com/dobyte/due/v2/core/pool"
	"github.com/dobyte/due/v2/etc"
	"github.com/dobyte/due/v2/log"
	"github.com/dobyte/jwt"
)

var factory = pool.NewFactory(func(name string) (*JWT, error) {
	return NewInstance(fmt.Sprintf("etc.jwt.%s", name))
})

type (
	JWT     = jwt.JWT
	Token   = jwt.Token
	Payload = jwt.Payload
)

type Config struct {
	Issuer        string       `json:"issuer"`
	SecretKey     string       `json:"secretKey"`
	IdentityKey   string       `json:"identityKey"`
	Locations     string       `json:"locations"`
	ValidDuration int          `json:"validDuration"`
	Store         *StoreConfig `json:"store"`
}

// Instance JWT实例
func Instance(name ...string) *JWT {
	var (
		err error
		ins *JWT
	)

	if len(name) == 0 {
		ins, err = factory.Get("default")
	} else {
		ins, err = factory.Get(name[0])
	}

	if err != nil {
		log.Fatalf("create jwt instance failed: %v", err)
	}

	return ins
}

// NewInstance 创建实例
func NewInstance[T string | Config | *Config](config T) (*JWT, error) {
	var (
		conf *Config
		v    any = config
	)

	switch c := v.(type) {
	case string:
		conf = &Config{
			Issuer:        etc.Get(fmt.Sprintf("%s.issuer", c)).String(),
			SecretKey:     etc.Get(fmt.Sprintf("%s.secretKey", c)).String(),
			IdentityKey:   etc.Get(fmt.Sprintf("%s.identityKey", c)).String(),
			Locations:     etc.Get(fmt.Sprintf("%s.locations", c)).String(),
			ValidDuration: etc.Get(fmt.Sprintf("%s.validDuration", c)).Int(),
		}

		if etc.Has(fmt.Sprintf("%s.store", c)) {
			conf.Store = &StoreConfig{
				Addrs:        etc.Get(fmt.Sprintf("%s.store.addrs", c)).Strings(),
				DB:           etc.Get(fmt.Sprintf("%s.store.db", c)).Int(),
				CertFile:     etc.Get(fmt.Sprintf("%s.store.certFile", c)).String(),
				KeyFile:      etc.Get(fmt.Sprintf("%s.store.keyFile", c)).String(),
				CaFile:       etc.Get(fmt.Sprintf("%s.store.caFile", c)).String(),
				Username:     etc.Get(fmt.Sprintf("%s.store.username", c)).String(),
				Password:     etc.Get(fmt.Sprintf("%s.store.password", c)).String(),
				MaxRetries:   etc.Get(fmt.Sprintf("%s.store.maxRetries", c), 3).Int(),
				PoolSize:     etc.Get(fmt.Sprintf("%s.store.poolSize", c), 200).Int(),
				MinIdleConns: etc.Get(fmt.Sprintf("%s.store.minIdleConns", c), 20).Int(),
				DialTimeout:  etc.Get(fmt.Sprintf("%s.store.dialTimeout", c), "3s").Duration(),
				IdleTimeout:  etc.Get(fmt.Sprintf("%s.store.idleTimeout", c)).Duration(),
				ReadTimeout:  etc.Get(fmt.Sprintf("%s.store.readTimeout", c), "1s").Duration(),
				WriteTimeout: etc.Get(fmt.Sprintf("%s.store.writeTimeout", c), "1s").Duration(),
			}
		}
	case Config:
		conf = &c
	case *Config:
		conf = c
	}

	opts := make([]jwt.Option, 0, 6)
	opts = append(opts, jwt.WithIssuer(conf.Issuer))
	opts = append(opts, jwt.WithIdentityKey(conf.IdentityKey))
	opts = append(opts, jwt.WithSecretKey(conf.SecretKey))
	opts = append(opts, jwt.WithValidDuration(conf.ValidDuration))
	opts = append(opts, jwt.WithLookupLocations(conf.Locations))

	if conf.Store != nil {
		store, err := NewStore(conf.Store)
		if err != nil {
			return nil, err
		}

		opts = append(opts, jwt.WithStore(store))
	}

	return jwt.NewJWT(opts...)
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109

文件位置:third-jwt-example/jwt/store.go (opens new window)

package jwt

import (
	"context"
	"time"

	"github.com/dobyte/due/v2/core/tls"
	"github.com/dobyte/due/v2/utils/xconv"
	"github.com/go-redis/redis/v8"
)

type StoreConfig struct {
	Addrs        []string      `json:"addrs"`
	DB           int           `json:"db"`
	Username     string        `json:"username"`
	Password     string        `json:"password"`
	CertFile     string        `json:"certFile"`
	KeyFile      string        `json:"keyFile"`
	CaFile       string        `json:"caFile"`
	MaxRetries   int           `json:"maxRetries"`
	PoolSize     int           `json:"poolSize"`
	MinIdleConns int           `json:"minIdleConns"`
	IdleTimeout  time.Duration `json:"idleTimeout"`
	DialTimeout  time.Duration `json:"dialTimeout"`
	ReadTimeout  time.Duration `json:"readTimeout"`
	WriteTimeout time.Duration `json:"writeTimeout"`
}

type Store struct {
	redis redis.UniversalClient
}

func NewStore(conf *StoreConfig) (*Store, error) {
	options := &redis.UniversalOptions{
		Addrs:        conf.Addrs,
		DB:           conf.DB,
		Username:     conf.Username,
		Password:     conf.Password,
		MaxRetries:   conf.MaxRetries,
		PoolSize:     conf.PoolSize,
		MinIdleConns: conf.MinIdleConns,
		IdleTimeout:  conf.IdleTimeout,
		DialTimeout:  conf.DialTimeout,
		ReadTimeout:  conf.ReadTimeout,
		WriteTimeout: conf.WriteTimeout,
	}

	if conf.CertFile != "" && conf.KeyFile != "" && conf.CaFile != "" {
		if tlsConfig, err := tls.MakeRedisTLSConfig(conf.CertFile, conf.KeyFile, conf.CaFile); err != nil {
			return nil, err
		} else {
			options.TLSConfig = tlsConfig
		}
	}

	return &Store{redis: redis.NewUniversalClient(options)}, nil
}

func (s *Store) Get(ctx context.Context, key any) (any, error) {
	return s.redis.Get(ctx, xconv.String(key)).Result()
}

func (s *Store) Set(ctx context.Context, key any, value any, duration time.Duration) error {
	return s.redis.Set(ctx, xconv.String(key), value, duration).Err()
}

func (s *Store) Remove(ctx context.Context, keys ...any) (value any, err error) {
	list := make([]string, 0, len(keys))
	for _, key := range keys {
		list = append(list, xconv.String(key))
	}

	return s.redis.Del(ctx, list...).Result()
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
  1. 配置示例

文件位置:third-jwt-example/etc/etc.toml (opens new window)

[jwt.default]
	# jwt发行方
	issuer = "due"
	# 过期时间
	validDuration = 7200
	# 秘钥KEY
	secretKey = "xxxx"
	# 身份认证KEY
	identityKey = "uid"
	# TOKEN查找位置
	locations = "query:token,header:Authorization"
	# 存储组件
	[jwt.default.store]
		# 连接地址
        addrs = ["127.0.0.1:6379"]
        # 数据库号,默认为0
        db = 0
        # 用户名,默认空
        username = ""
        # 密码,默认空
        password = ""
        # 私钥文件
        keyFile = ""
        # 证书文件
        certFile = ""
        # CA证书文件
        caFile = ""
        # 网络相关的错误最大重试次数,默认3次
        maxRetries = 3
        # 最大连接池限制,默认为200个连接
        poolSize = 200
        # 最小空闲连接数,默认10个连接
        minIdleConns = 10
        # 拨号超时时间,支持单位:纳秒(ns)、微秒(us | µs)、毫秒(ms)、秒(s)、分(m)、小时(h)、天(d)。默认5s
        dialTimeout = "5s"
        # 连接最大空闲时间,支持单位:纳秒(ns)、微秒(us | µs)、毫秒(ms)、秒(s)、分(m)、小时(h)、天(d)。默认0s
        idleTimeout = "0s"
        # 读超时,支持单位:纳秒(ns)、微秒(us | µs)、毫秒(ms)、秒(s)、分(m)、小时(h)、天(d)。默认1s
        readTimeout = "1s"
        # 写超时,支持单位:纳秒(ns)、微秒(us | µs)、毫秒(ms)、秒(s)、分(m)、小时(h)、天(d)。默认1s
        writeTimeout = "1s"
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
  1. 组件调用

文件位置:third-jwt-example/main.go (opens new window)

package main

import (
	"third-jwt-example/jwt"

	"github.com/dobyte/due/v2/log"
)

func main() {
	ins := jwt.Instance("default")

	token, err := ins.GenerateToken(jwt.Payload{
		ins.IdentityKey(): 1,
		"nickname":        "fuxiao",
	})
	if err != nil {
		log.Errorf("generate token error: %v", err)
	} else {
		log.Infof("generate token success: %s", token.Token)
	}

	payload, err := ins.ExtractPayload(token.Token)
	if err != nil {
		log.Errorf("extract payload error: %v", err)
	} else {
		log.Infof("extract payload success: %v", payload)
	}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28