101 lines
2.9 KiB
Go
101 lines
2.9 KiB
Go
|
|
// Package util provide some utility functions
|
|||
|
|
package util
|
|||
|
|
|
|||
|
|
import (
|
|||
|
|
"crypto/hmac"
|
|||
|
|
"crypto/sha256"
|
|||
|
|
"encoding/base64"
|
|||
|
|
"fmt"
|
|||
|
|
"os"
|
|||
|
|
"strings"
|
|||
|
|
"time"
|
|||
|
|
)
|
|||
|
|
|
|||
|
|
// GenerateClientToken define func of generate a secure token for client identification
|
|||
|
|
func GenerateClientToken(host string, serviceName string) (string, error) {
|
|||
|
|
uniqueID := fmt.Sprintf("%d", time.Now().UnixNano())
|
|||
|
|
clientInfo := fmt.Sprintf("host=%s;service=%s;id=%s", host, serviceName, uniqueID)
|
|||
|
|
secretKey := os.Getenv("TOKEN_SECRET_KEY")
|
|||
|
|
if secretKey == "" {
|
|||
|
|
return "", fmt.Errorf("TOKEN_SECRET_KEY environment variable not set")
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
mac := hmac.New(sha256.New, []byte(secretKey))
|
|||
|
|
mac.Write([]byte(clientInfo))
|
|||
|
|
signature := mac.Sum(nil)
|
|||
|
|
|
|||
|
|
token := fmt.Sprintf("%s.%s",
|
|||
|
|
base64.URLEncoding.EncodeToString([]byte(clientInfo)),
|
|||
|
|
base64.URLEncoding.EncodeToString(signature))
|
|||
|
|
|
|||
|
|
return token, nil
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// verifyClientToken define func of verify the client token
|
|||
|
|
func verifyClientToken(token string, secretKey string) (bool, string, error) {
|
|||
|
|
parts := strings.Split(token, ".")
|
|||
|
|
if len(parts) != 2 {
|
|||
|
|
return false, "", fmt.Errorf("invalid token format")
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
encodedClientInfo := parts[0]
|
|||
|
|
encodedSignature := parts[1]
|
|||
|
|
|
|||
|
|
clientInfoBytes, err := base64.URLEncoding.DecodeString(encodedClientInfo)
|
|||
|
|
if err != nil {
|
|||
|
|
return false, "", fmt.Errorf("failed to decode client info: %w", err)
|
|||
|
|
}
|
|||
|
|
signatureBytes, err := base64.URLEncoding.DecodeString(encodedSignature)
|
|||
|
|
if err != nil {
|
|||
|
|
return false, "", fmt.Errorf("failed to decode signature: %w", err)
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
mac := hmac.New(sha256.New, []byte(secretKey))
|
|||
|
|
mac.Write(clientInfoBytes)
|
|||
|
|
expectedSignature := mac.Sum(nil)
|
|||
|
|
|
|||
|
|
if !hmac.Equal(signatureBytes, expectedSignature) {
|
|||
|
|
return false, "", nil // 签名不匹配,token 无效
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
clientInfo := string(clientInfoBytes)
|
|||
|
|
return true, clientInfo, nil
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// --- 示例使用 ---
|
|||
|
|
|
|||
|
|
func main() {
|
|||
|
|
// 模拟获取客户端信息
|
|||
|
|
// 实际应用中,host 可以通过获取本地主机名,serviceName 可以是当前服务的标识。
|
|||
|
|
host, err := os.Hostname()
|
|||
|
|
if err != nil {
|
|||
|
|
fmt.Println("Error getting hostname:", err)
|
|||
|
|
host = "unknown-host"
|
|||
|
|
}
|
|||
|
|
serviceName := "my-web-service" // 替换为你的服务名
|
|||
|
|
|
|||
|
|
// 设置一个示例密钥(在实际应用中,请从环境变量加载)
|
|||
|
|
// export TOKEN_SECRET_KEY="your-super-secret-key-here"
|
|||
|
|
os.Setenv("TOKEN_SECRET_KEY", "my-super-secret-and-long-key-for-hmac")
|
|||
|
|
|
|||
|
|
token, err := GenerateClientToken(host, serviceName)
|
|||
|
|
if err != nil {
|
|||
|
|
fmt.Println("Error generating token:", err)
|
|||
|
|
return
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
fmt.Println("Generated Token:", token)
|
|||
|
|
|
|||
|
|
// --- 验证 Token (示例) ---
|
|||
|
|
// 在接收方,你需要实现一个验证函数
|
|||
|
|
isValid, clientInfo, err := verifyClientToken(token, os.Getenv("TOKEN_SECRET_KEY"))
|
|||
|
|
if err != nil {
|
|||
|
|
fmt.Println("Error verifying token:", err)
|
|||
|
|
} else if isValid {
|
|||
|
|
fmt.Println("Token is valid!")
|
|||
|
|
fmt.Println("Client Info:", clientInfo) // 包含 host, service, id
|
|||
|
|
} else {
|
|||
|
|
fmt.Println("Token is invalid.")
|
|||
|
|
}
|
|||
|
|
}
|