init: 自 zomaintain/backend/zonat 平移独立成库; module path 改为 git.zeroonesoft.cn/golib/zonat; 内部 import 同步重写

This commit is contained in:
w11
2026-09-19 18:55:43 +08:00
commit 984041f1a0
20 changed files with 2427 additions and 0 deletions
+60
View File
@@ -0,0 +1,60 @@
// jwt.go agent 登录令牌的 JWT 校验与签发(HS256)。
// 正式部署由签发方(如 cloud)生成 token 下发给各隧道;节点只做校验。
// SignToken 供 cmd/node -print-token(运维手工签发)与测试使用。
package node
import (
"errors"
"fmt"
"time"
"github.com/golang-jwt/jwt/v5"
)
// verifyToken 校验 agent 登录 JWT:
// - HS256/384/512 签名(显式白名单,拒绝 none 与其它算法)
// - 必须带 exp 且未过期
// - 若带 agentId 或 sub claim,则必须与登录 AgentId 一致(防跨 agent 冒用)
func verifyToken(token, secret, agentId string) error {
if secret == "" {
return errors.New("节点未配置JWT密钥")
}
parsed, err := jwt.Parse(token, func(t *jwt.Token) (any, error) {
if _, ok := t.Method.(*jwt.SigningMethodHMAC); !ok {
return nil, fmt.Errorf("签名算法不允许:%v", t.Header["alg"])
}
return []byte(secret), nil
}, jwt.WithValidMethods([]string{
jwt.SigningMethodHS256.Alg(),
jwt.SigningMethodHS384.Alg(),
jwt.SigningMethodHS512.Alg(),
}), jwt.WithExpirationRequired())
if err != nil {
return err
}
claims, ok := parsed.Claims.(jwt.MapClaims)
if !ok {
return errors.New("claims类型错误")
}
for _, k := range []string{"agentId", "sub"} {
if v, ok := claims[k].(string); ok && v != "" && v != agentId {
return fmt.Errorf("token %s 与登录agentId不一致", k)
}
}
return nil
}
// SignToken 用节点密钥签发一张 agent 登录 JWT。
// ttl<=0 会得到立即过期的 token(仅测试构造过期样例时有意义)。
func SignToken(secret, agentId string, ttl time.Duration) (string, error) {
if secret == "" {
return "", errors.New("未配置JWT密钥")
}
now := time.Now()
claims := jwt.MapClaims{
"agentId": agentId,
"iat": now.Unix(),
"exp": now.Add(ttl).Unix(),
}
return jwt.NewWithClaims(jwt.SigningMethodHS256, claims).SignedString([]byte(secret))
}