Feat: jwt token加密算法由ES256更换为HS256

This commit is contained in:
Eigeen 2022-04-03 13:29:47 +08:00
parent ec4c957b25
commit e1f6d3c822
8 changed files with 34 additions and 188 deletions

View File

@ -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

View File

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

View File

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

5
config/defaults.go Normal file
View File

@ -0,0 +1,5 @@
package config
var DefCfg = Config{
Version: "0.0.1-dev",
}

View File

@ -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
} }

View File

@ -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 {

View File

@ -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
} }

View File

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