add func of generate service token

This commit is contained in:
douxu 2025-09-12 17:12:02 +08:00
parent e670720a96
commit 0c09e7bd25
12 changed files with 174 additions and 14 deletions

View File

@ -7,14 +7,20 @@ import (
"github.com/spf13/viper" "github.com/spf13/viper"
) )
// BaseConfig define config stuct of base params config // BaseConfig define config struct of base params config
type BaseConfig struct { type BaseConfig struct {
GridID int64 `mapstructure:"grid_id"` GridID int64 `mapstructure:"grid_id"`
ZoneID int64 `mapstructure:"zone_id"` ZoneID int64 `mapstructure:"zone_id"`
StationID int64 `mapstructure:"station_id"` StationID int64 `mapstructure:"station_id"`
} }
// KafkaConfig define config stuct of kafka config // ServiceConfig define config struct of service config
type ServiceConfig struct {
ServiceName string `mapstructure:"service_name"`
SecretKey string `mapstructure:"secret_key"`
}
// KafkaConfig define config struct of kafka config
type KafkaConfig struct { type KafkaConfig struct {
Servers string `mapstructure:"Servers"` Servers string `mapstructure:"Servers"`
GroupID string `mapstructure:"group_id"` GroupID string `mapstructure:"group_id"`
@ -24,7 +30,7 @@ type KafkaConfig struct {
ReadMessageTimeDuration string `mapstructure:"read_message_time_duration"` ReadMessageTimeDuration string `mapstructure:"read_message_time_duration"`
} }
// PostgresConfig define config stuct of postgres config // PostgresConfig define config struct of postgres config
type PostgresConfig struct { type PostgresConfig struct {
Port int `mapstructure:"port"` Port int `mapstructure:"port"`
Host string `mapstructure:"host"` Host string `mapstructure:"host"`
@ -33,7 +39,7 @@ type PostgresConfig struct {
Password string `mapstructure:"password"` Password string `mapstructure:"password"`
} }
// LoggerConfig define config stuct of zap logger config // LoggerConfig define config struct of zap logger config
type LoggerConfig struct { type LoggerConfig struct {
Mode string `mapstructure:"mode"` Mode string `mapstructure:"mode"`
Level string `mapstructure:"level"` Level string `mapstructure:"level"`
@ -44,7 +50,7 @@ type LoggerConfig struct {
Compress bool `mapstructure:"compress"` Compress bool `mapstructure:"compress"`
} }
// RedisConfig define config stuct of redis config // RedisConfig define config struct of redis config
type RedisConfig struct { type RedisConfig struct {
Addr string `mapstructure:"addr"` Addr string `mapstructure:"addr"`
Password string `mapstructure:"password"` Password string `mapstructure:"password"`
@ -53,13 +59,13 @@ type RedisConfig struct {
Timeout int `mapstructure:"timeout"` Timeout int `mapstructure:"timeout"`
} }
// AntsConfig define config stuct of ants pool config // AntsConfig define config struct of ants pool config
type AntsConfig struct { type AntsConfig struct {
ParseConcurrentQuantity int `mapstructure:"parse_concurrent_quantity"` // parse comtrade file concurrent quantity ParseConcurrentQuantity int `mapstructure:"parse_concurrent_quantity"` // parse comtrade file concurrent quantity
RTDReceiveConcurrentQuantity int `mapstructure:"rtd_receive_concurrent_quantity"` // polling real time data concurrent quantity RTDReceiveConcurrentQuantity int `mapstructure:"rtd_receive_concurrent_quantity"` // polling real time data concurrent quantity
} }
// DataRTConfig define config stuct of data runtime server api config // DataRTConfig define config struct of data runtime server api config
type DataRTConfig struct { type DataRTConfig struct {
Host string `mapstructure:"host"` Host string `mapstructure:"host"`
Port int64 `mapstructure:"port"` Port int64 `mapstructure:"port"`
@ -67,9 +73,10 @@ type DataRTConfig struct {
Method string `mapstructure:"polling_api_method"` Method string `mapstructure:"polling_api_method"`
} }
// ModelRTConfig define config stuct of model runtime server // ModelRTConfig define config struct of model runtime server
type ModelRTConfig struct { type ModelRTConfig struct {
BaseConfig `mapstructure:"base"` BaseConfig `mapstructure:"base"`
ServiceConfig `mapstructure:"service"`
PostgresConfig `mapstructure:"postgres"` PostgresConfig `mapstructure:"postgres"`
KafkaConfig `mapstructure:"kafka"` KafkaConfig `mapstructure:"kafka"`
LoggerConfig `mapstructure:"logger"` LoggerConfig `mapstructure:"logger"`

View File

@ -57,6 +57,11 @@ base:
zone_id: 1 zone_id: 1
station_id: 1 station_id: 1
# modelRT service config
service:
service_name: "modelRT"
secret_key: "modelRT"
# dataRT api config # dataRT api config
dataRT: dataRT:
host: "http://127.0.0.1" host: "http://127.0.0.1"

View File

@ -1,3 +1,4 @@
// Package handler provides HTTP handlers for various endpoints.
package handler package handler
import ( import (
@ -14,6 +15,23 @@ import (
// MeasurementGetHandler retrieves the value of measurement data // MeasurementGetHandler retrieves the value of measurement data
func MeasurementGetHandler(c *gin.Context) { func MeasurementGetHandler(c *gin.Context) {
var request network.MeasurementGetRequest var request network.MeasurementGetRequest
var clientToken string
// TODO 优化client_token获取
token, ok := c.Get("client_token")
if ok {
// 将 token 断言为 ClientToken 类型
clientToken, isClientToken := token.(string)
if !isClientToken {
// TODO 优化相关报错输出
c.JSON(http.StatusOK, gin.H{
"message": "Request processed successfully",
"token": clientToken,
})
return
}
clientToken = token.(string)
}
if err := c.ShouldBindJSON(&request); err != nil { if err := c.ShouldBindJSON(&request); err != nil {
logger.Error(c, "Failed to unmarshal measurement get request", "error", err) logger.Error(c, "Failed to unmarshal measurement get request", "error", err)
@ -23,9 +41,8 @@ func MeasurementGetHandler(c *gin.Context) {
}) })
return return
} }
// token 当前客户端的唯一标识token,用于区分不同的客户端。 // token 当前客户端的唯一标识token,用于区分不同的客户端。
zset := diagram.NewRedisZSet(c, request.MeasurementToken, "token", 10, false) zset := diagram.NewRedisZSet(c, request.MeasurementToken, clientToken, 0, false)
points, err := zset.ZRANGE(request.MeasurementToken, 0, -1) points, err := zset.ZRANGE(request.MeasurementToken, 0, -1)
if err != nil { if err != nil {
logger.Error(c, "failed to get measurement data from redis", "measurement_token", request.MeasurementToken, "error", err) logger.Error(c, "failed to get measurement data from redis", "measurement_token", request.MeasurementToken, "error", err)

16
main.go
View File

@ -21,6 +21,7 @@ import (
"modelRT/middleware" "modelRT/middleware"
"modelRT/pool" "modelRT/pool"
"modelRT/router" "modelRT/router"
"modelRT/util"
realtimedata "modelRT/real-time-data" realtimedata "modelRT/real-time-data"
@ -58,6 +59,19 @@ func main() {
logger.InitLoggerInstance(modelRTConfig.LoggerConfig) logger.InitLoggerInstance(modelRTConfig.LoggerConfig)
modelRTConfig = config.ReadAndInitConfig(*modelRTConfigDir, *modelRTConfigName, *modelRTConfigType) modelRTConfig = config.ReadAndInitConfig(*modelRTConfigDir, *modelRTConfigName, *modelRTConfigType)
hostName, err := os.Hostname()
if err != nil {
logger.Error(ctx, "get host name failed", "error", err)
panic(err)
}
serviceToken, err := util.GenerateClientToken(hostName, modelRTConfig.ServiceConfig.ServiceName)
if err != nil {
logger.Error(ctx, "generate client token failed", "error", err)
panic(err)
}
// init postgresDBClient // init postgresDBClient
postgresDBClient = database.InitPostgresDBInstance(ctx, modelRTConfig.PostgresDBURI) postgresDBClient = database.InitPostgresDBInstance(ctx, modelRTConfig.PostgresDBURI)
@ -123,7 +137,7 @@ func main() {
// go realtimedata.RealTimeDataComputer(ctx, nil, []string{}, "") // go realtimedata.RealTimeDataComputer(ctx, nil, []string{}, "")
engine := gin.New() engine := gin.New()
router.RegisterRoutes(engine) router.RegisterRoutes(engine, serviceToken)
server := http.Server{ server := http.Server{
Addr: ":8080", Addr: ":8080",
Handler: engine, Handler: engine,

11
middleware/token.go Normal file
View File

@ -0,0 +1,11 @@
package middleware
import "github.com/gin-gonic/gin"
// SetTokenMiddleware define a middleware for set token in context
func SetTokenMiddleware(clientToken string) gin.HandlerFunc {
return func(c *gin.Context) {
c.Set("client_token", clientToken)
c.Next()
}
}

View File

@ -3,5 +3,6 @@ package network
// MeasurementGetRequest defines the request payload for getting an measurement // MeasurementGetRequest defines the request payload for getting an measurement
type MeasurementGetRequest struct { type MeasurementGetRequest struct {
MeasurementID int64 `json:"measurement_id"`
MeasurementToken string `json:"token"` MeasurementToken string `json:"token"`
} }

View File

@ -3,12 +3,14 @@ package router
import ( import (
"modelRT/handler" "modelRT/handler"
"modelRT/middleware"
"github.com/gin-gonic/gin" "github.com/gin-gonic/gin"
) )
// registerMeasurementRoutes define func of register measurement routes // registerMeasurementRoutes define func of register measurement routes
func registerMeasurementRoutes(rg *gin.RouterGroup) { func registerMeasurementRoutes(rg *gin.RouterGroup, clientToken string) {
g := rg.Group("/measurement/") g := rg.Group("/measurement/")
rg.Use(middleware.SetTokenMiddleware(clientToken))
g.GET("load", handler.MeasurementGetHandler) g.GET("load", handler.MeasurementGetHandler)
} }

View File

@ -16,11 +16,11 @@ func init() {
} }
// RegisterRoutes define func of register all routes // RegisterRoutes define func of register all routes
func RegisterRoutes(engine *gin.Engine) { func RegisterRoutes(engine *gin.Engine, clientToken string) {
// use global middlewares // use global middlewares
engine.Use(middleware.StartTrace(), limiter.Middleware) engine.Use(middleware.StartTrace(), limiter.Middleware)
routeGroup := engine.Group("") routeGroup := engine.Group("")
registerDiagramRoutes(routeGroup) registerDiagramRoutes(routeGroup)
registerAttrRoutes(routeGroup) registerAttrRoutes(routeGroup)
registerMeasurementRoutes(routeGroup) registerMeasurementRoutes(routeGroup, clientToken)
} }

View File

@ -1,3 +1,4 @@
// Package util provide some utility functions
package util package util
import ( import (

View File

@ -1,3 +1,4 @@
// Package util provide some utility functions
package util package util
import ( import (

100
util/token.go Normal file
View File

@ -0,0 +1,100 @@
// 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.")
}
}

View File

@ -1,3 +1,4 @@
// Package util provide some utility functions
package util package util
import ( import (