✨Feat: jwt token加密算法由ES256更换为HS256
This commit is contained in:
parent
ec4c957b25
commit
e1f6d3c822
|
@ -1,4 +1,4 @@
|
||||||
# 应用版本,应与当前版本一致
|
# 应用版本
|
||||||
version: {{ .Version }}
|
version: {{ .Version }}
|
||||||
|
|
||||||
# 应用配置
|
# 应用配置
|
||||||
|
@ -15,9 +15,8 @@ security:
|
||||||
expire: 14400
|
expire: 14400
|
||||||
# Refresh Token过期时间 (默认24小时)
|
# Refresh Token过期时间 (默认24小时)
|
||||||
refresh-expire: 86400
|
refresh-expire: 86400
|
||||||
# 以下字段指定key的文件路径
|
# 请确保签名密钥安全性足够高,建议使用高强度随机字符串
|
||||||
private-key: id_ecdsa
|
secret: {{ .Security.Jwt.Secret }}
|
||||||
public-key: id_ecdsa.pub
|
|
||||||
# 允许额外的跨域请求
|
# 允许额外的跨域请求
|
||||||
allow-CORS:
|
allow-CORS:
|
||||||
- "*"
|
- "*"
|
||||||
|
@ -31,7 +30,7 @@ database:
|
||||||
# mysql https://github.com/go-sql-driver/mysql#dsn-data-source-name
|
# mysql https://github.com/go-sql-driver/mysql#dsn-data-source-name
|
||||||
# postgres https://github.com/lib/pq
|
# postgres https://github.com/lib/pq
|
||||||
|
|
||||||
|
# with unit second
|
||||||
max-idle-conns: 20
|
max-idle-conns: 20
|
||||||
max-open-conns: 50
|
max-open-conns: 50
|
||||||
# with unit sec
|
|
||||||
conn-max-life-time: 30
|
conn-max-life-time: 30
|
||||||
|
|
|
@ -22,8 +22,7 @@ type Config struct {
|
||||||
Jwt struct {
|
Jwt struct {
|
||||||
Expire uint32
|
Expire uint32
|
||||||
RefreshExpire uint32 `yaml:"refresh-expire"`
|
RefreshExpire uint32 `yaml:"refresh-expire"`
|
||||||
PrivateKey string `yaml:"private-key"`
|
Secret string
|
||||||
PublicKey string `yaml:"public-key"`
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Database struct {
|
Database struct {
|
||||||
|
@ -39,6 +38,9 @@ var configFull string
|
||||||
|
|
||||||
// 加载配置文件
|
// 加载配置文件
|
||||||
func SetupConfig() {
|
func SetupConfig() {
|
||||||
|
// 加载默认配置
|
||||||
|
Cfg = DefCfg
|
||||||
|
|
||||||
// 读取配置文件,若不存在则创建
|
// 读取配置文件,若不存在则创建
|
||||||
if isExist := utils.FileExist("config.yml"); !isExist {
|
if isExist := utils.FileExist("config.yml"); !isExist {
|
||||||
newCfgFile()
|
newCfgFile()
|
||||||
|
@ -82,6 +84,9 @@ func parseConfigTmpl() []byte {
|
||||||
|
|
||||||
// 创建配置文件
|
// 创建配置文件
|
||||||
func newCfgFile() {
|
func newCfgFile() {
|
||||||
|
// 生成Key
|
||||||
|
Cfg.Security.Jwt.Secret = utils.NewKey()
|
||||||
|
|
||||||
// 解析配置文件模板
|
// 解析配置文件模板
|
||||||
tmplBytes := parseConfigTmpl()
|
tmplBytes := parseConfigTmpl()
|
||||||
|
|
||||||
|
|
|
@ -3,5 +3,5 @@ package config
|
||||||
import "testing"
|
import "testing"
|
||||||
|
|
||||||
func TestSetupConfig(t *testing.T) {
|
func TestSetupConfig(t *testing.T) {
|
||||||
Cfg.Version = "0.0.1-dev"
|
SetupConfig()
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,5 @@
|
||||||
|
package config
|
||||||
|
|
||||||
|
var DefCfg = Config{
|
||||||
|
Version: "0.0.1-dev",
|
||||||
|
}
|
|
@ -1,15 +1,8 @@
|
||||||
package common
|
package common
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"crypto/ecdsa"
|
|
||||||
"crypto/elliptic"
|
|
||||||
"crypto/rand"
|
|
||||||
"drive-linked/config"
|
"drive-linked/config"
|
||||||
"drive-linked/pkg/utils"
|
|
||||||
"github.com/golang-jwt/jwt/v4"
|
"github.com/golang-jwt/jwt/v4"
|
||||||
"github.com/kataras/golog"
|
|
||||||
"io/ioutil"
|
|
||||||
"os"
|
|
||||||
"time"
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -18,71 +11,15 @@ type JwtClaims struct {
|
||||||
jwt.RegisteredClaims
|
jwt.RegisteredClaims
|
||||||
}
|
}
|
||||||
|
|
||||||
var ECDSAKey *ecdsa.PrivateKey
|
|
||||||
|
|
||||||
// 生成ES256密钥对,并保存在文件中
|
|
||||||
func init() {
|
|
||||||
// 密钥对存在时跳过
|
|
||||||
//TODO:bug:会重复生成key
|
|
||||||
if isExist := utils.FileExist("id_ecdsa") && utils.FileExist("id_ecdsa.pub"); isExist {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
key, err := newES256Key()
|
|
||||||
if err != nil {
|
|
||||||
golog.Fatal("生成ES256密钥错误")
|
|
||||||
}
|
|
||||||
|
|
||||||
// 写入至文件
|
|
||||||
pubKeyBytes, err := utils.EncodePublicKey(&key.PublicKey)
|
|
||||||
if err != nil {
|
|
||||||
golog.Fatal(err)
|
|
||||||
}
|
|
||||||
priKeyBytes, err := utils.EncodePrivateKey(key)
|
|
||||||
if err != nil {
|
|
||||||
golog.Fatal(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
priKeyFile, err := os.OpenFile("id_ecdsa", os.O_CREATE, 0600)
|
|
||||||
if err != nil {
|
|
||||||
golog.Fatal(err)
|
|
||||||
}
|
|
||||||
pubKeyFile, err := os.OpenFile("id_ecdsa.pub", os.O_CREATE, 0655)
|
|
||||||
if err != nil {
|
|
||||||
golog.Fatal(err)
|
|
||||||
}
|
|
||||||
priKeyFile.Write(priKeyBytes)
|
|
||||||
pubKeyFile.Write(pubKeyBytes)
|
|
||||||
}
|
|
||||||
|
|
||||||
func newES256Key() (key *ecdsa.PrivateKey, err error) {
|
|
||||||
key, err = ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
|
|
||||||
return key, err
|
|
||||||
}
|
|
||||||
|
|
||||||
func LoadKey() error {
|
|
||||||
if ECDSAKey != nil {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
priKeyBytes, err := ioutil.ReadFile(config.Cfg.Security.Jwt.PrivateKey)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
key, err := utils.DecodePrivateKey(priKeyBytes)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
ECDSAKey = key
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
//TODO:token解密验证
|
//TODO:token解密验证
|
||||||
func ValidateLogin(token string) error {
|
func ValidateLogin(token string) error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewToken(auds ...string) (string, error) {
|
func NewToken(auds ...string) (string, error) {
|
||||||
|
if len(auds) == 0 {
|
||||||
|
auds = []string{"nonAudience"}
|
||||||
|
}
|
||||||
// Create the claims
|
// Create the claims
|
||||||
claims := JwtClaims{
|
claims := JwtClaims{
|
||||||
"bar",
|
"bar",
|
||||||
|
@ -93,12 +30,12 @@ func NewToken(auds ...string) (string, error) {
|
||||||
NotBefore: jwt.NewNumericDate(time.Now()),
|
NotBefore: jwt.NewNumericDate(time.Now()),
|
||||||
Issuer: "drivelinked",
|
Issuer: "drivelinked",
|
||||||
Subject: "login",
|
Subject: "login",
|
||||||
Audience: []string{"eigeen"},
|
Audience: auds,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
token := jwt.NewWithClaims(jwt.SigningMethodES256, claims)
|
token := jwt.NewWithClaims(jwt.SigningMethodHS256, claims)
|
||||||
ss, err := token.SignedString(ECDSAKey)
|
ss, err := token.SignedString([]byte(config.Cfg.Security.Jwt.Secret))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", err
|
return "", err
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,10 +7,6 @@ import (
|
||||||
|
|
||||||
func TestNewToken(t *testing.T) {
|
func TestNewToken(t *testing.T) {
|
||||||
config.SetupConfig()
|
config.SetupConfig()
|
||||||
err := LoadKey()
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
token, err := NewToken("eigeen")
|
token, err := NewToken("eigeen")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
|
@ -1,84 +1,20 @@
|
||||||
package utils
|
package utils
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"crypto/ecdsa"
|
"crypto/rand"
|
||||||
"crypto/x509"
|
"encoding/hex"
|
||||||
"encoding/pem"
|
"github.com/kataras/golog"
|
||||||
"errors"
|
|
||||||
"fmt"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
func DecodePublicKey(encodedKey []byte) (*ecdsa.PublicKey, error) {
|
// NewKey 生成Key
|
||||||
block, _ := pem.Decode(encodedKey)
|
func NewKey() string {
|
||||||
if block == nil || block.Type != "PUBLIC KEY" {
|
length := 48
|
||||||
return nil, fmt.Errorf("marshal: could not decode PEM block type %s", block.Type)
|
// HS256的key,其实就是个随机字符串生成器
|
||||||
|
key := make([]byte, length/2)
|
||||||
}
|
_, err := rand.Read(key)
|
||||||
|
|
||||||
pub, err := x509.ParsePKIXPublicKey(block.Bytes)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
golog.Fatal(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
ecdsaPub, ok := pub.(*ecdsa.PublicKey)
|
return hex.EncodeToString(key)
|
||||||
if !ok {
|
|
||||||
return nil, errors.New("marshal: data was not an ECDSA public key")
|
|
||||||
}
|
|
||||||
|
|
||||||
return ecdsaPub, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func EncodePublicKey(key *ecdsa.PublicKey) ([]byte, error) {
|
|
||||||
derBytes, err := x509.MarshalPKIXPublicKey(key)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
block := &pem.Block{
|
|
||||||
Type: "PUBLIC KEY",
|
|
||||||
Bytes: derBytes,
|
|
||||||
}
|
|
||||||
|
|
||||||
return pem.EncodeToMemory(block), nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func DecodePrivateKey(encodedKey []byte) (*ecdsa.PrivateKey, error) {
|
|
||||||
var skippedTypes []string
|
|
||||||
var block *pem.Block
|
|
||||||
|
|
||||||
for {
|
|
||||||
block, encodedKey = pem.Decode(encodedKey)
|
|
||||||
|
|
||||||
if block == nil {
|
|
||||||
return nil, fmt.Errorf("failed to find EC PRIVATE KEY in PEM data after skipping types %v", skippedTypes)
|
|
||||||
}
|
|
||||||
|
|
||||||
if block.Type == "EC PRIVATE KEY" {
|
|
||||||
break
|
|
||||||
} else {
|
|
||||||
skippedTypes = append(skippedTypes, block.Type)
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
privKey, err := x509.ParseECPrivateKey(block.Bytes)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
return privKey, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func EncodePrivateKey(key *ecdsa.PrivateKey) ([]byte, error) {
|
|
||||||
derKey, err := x509.MarshalECPrivateKey(key)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
keyBlock := &pem.Block{
|
|
||||||
Type: "EC PRIVATE KEY",
|
|
||||||
Bytes: derKey,
|
|
||||||
}
|
|
||||||
|
|
||||||
return pem.EncodeToMemory(keyBlock), nil
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,32 +0,0 @@
|
||||||
package utils
|
|
||||||
|
|
||||||
import (
|
|
||||||
"io/ioutil"
|
|
||||||
"testing"
|
|
||||||
)
|
|
||||||
|
|
||||||
func TestDecodePrivateKey(t *testing.T) {
|
|
||||||
priKeyStr, err := ioutil.ReadFile("../../config/id_ecdsa")
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
key, err := DecodePrivateKey(priKeyStr)
|
|
||||||
if err != nil {
|
|
||||||
t.Error(err)
|
|
||||||
}
|
|
||||||
t.Log(key)
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestDecodePublicKey(t *testing.T) {
|
|
||||||
priKeyStr, err := ioutil.ReadFile("../../config/id_ecdsa.pub")
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
key, err := DecodePublicKey(priKeyStr)
|
|
||||||
if err != nil {
|
|
||||||
t.Error(err)
|
|
||||||
}
|
|
||||||
t.Log(key)
|
|
||||||
}
|
|
Loading…
Reference in New Issue