feat: 新增 snowid(雪花ID+GORM钩子模板,吸收go-hua); wsc新增SessionClient会话层(吸收两端internal/cloud); jwtx重写为标准三段式HS256+密钥显式传参
This commit is contained in:
+8
-12
@@ -1,23 +1,19 @@
|
||||
# jwtx
|
||||
|
||||
轻量 HMAC-SHA256 令牌(JWT 风格,无第三方依赖),用于登录态/服务间鉴权。
|
||||
轻量 HS256 JWT(标准库实现,零外部依赖)。密钥显式传参,不落全局状态。
|
||||
|
||||
## 用法
|
||||
|
||||
```go
|
||||
import "git.zeroonesoft.cn/golib/zogo/jwtx"
|
||||
|
||||
token, err := jwtx.Sign(uid, 3600) // 签发,TTL 秒
|
||||
claims, err := jwtx.Parse(token) // 校验签名+过期,返回 *Claims{Uid, Exp, Msg}
|
||||
token, err := jwtx.Sign(secret, jwtx.Claims{UserID: 42, Role: "admin", Exp: time.Now().Add(time.Hour).Unix()})
|
||||
claims, err := jwtx.Parse(secret, token) // 签名错→ErrInvalidToken,过期→ErrExpired
|
||||
```
|
||||
|
||||
## 格式
|
||||
## 说明
|
||||
|
||||
`base64url(payload).base64url(hmac-sha256(payload))`,两段式(无 header 段),payload 为
|
||||
`Claims` JSON。
|
||||
|
||||
## 注意
|
||||
|
||||
- **签名密钥当前硬编码在包内**(`zo-maintain-jwt-secret-2026`)。仅适用于内网/测试场景;
|
||||
用于生产鉴权前应改为注入式(加 `SetSecret` 或从环境读取)。
|
||||
- 过期校验在 `Parse` 内完成(`Exp` 与当前时间比较)。
|
||||
- 标准三段式 `header.payload.signature`(HS256),`Claims{UserID, Role, Exp}` 为登录令牌
|
||||
通用三字段;需要更多业务字段时建议换成 `github.com/golang-jwt/jwt`,本包定位是
|
||||
零依赖场景的最小实现。
|
||||
- **密钥必须从配置/环境注入**,不要硬编码。
|
||||
|
||||
+41
-30
@@ -1,4 +1,7 @@
|
||||
// Package jwtx 轻量 JWT(HMAC-SHA256),无外部依赖。
|
||||
// Package jwtx 轻量 HS256 JWT(标准库实现,零外部依赖)。
|
||||
//
|
||||
// 密钥显式传入(不落包内全局状态),由调用方从配置注入。
|
||||
// Claims 载荷为登录令牌通用三字段(uid/role/exp),业务扩展字段请自行加签发方案。
|
||||
package jwtx
|
||||
|
||||
import (
|
||||
@@ -11,51 +14,59 @@ import (
|
||||
"time"
|
||||
)
|
||||
|
||||
// Claims 登录令牌载荷。
|
||||
var (
|
||||
// ErrInvalidToken token 无效。
|
||||
ErrInvalidToken = errors.New("无效的 token")
|
||||
// ErrExpired token 已过期。
|
||||
ErrExpired = errors.New("token 已过期")
|
||||
)
|
||||
|
||||
// Claims JWT 载荷(登录令牌通用字段)。
|
||||
type Claims struct {
|
||||
Uid int64 `json:"uid"`
|
||||
Exp int64 `json:"exp"`
|
||||
Msg string `json:"msg,omitempty"`
|
||||
UserID int64 `json:"uid"`
|
||||
Role string `json:"role"` // admin / user / console ...
|
||||
Exp int64 `json:"exp"` // 过期时间(Unix 秒)
|
||||
}
|
||||
|
||||
var secret = []byte("zo-maintain-jwt-secret-2026")
|
||||
|
||||
// Sign 签发 token(TTL 秒)。
|
||||
func Sign(uid int64, ttlSeconds int64) (string, error) {
|
||||
payload, err := json.Marshal(Claims{Uid: uid, Exp: time.Now().Add(time.Duration(ttlSeconds) * time.Second).Unix()})
|
||||
// Sign 生成 HS256 token。
|
||||
func Sign(secret string, claims Claims) (string, error) {
|
||||
header := base64.RawURLEncoding.EncodeToString([]byte(`{"alg":"HS256","typ":"JWT"}`))
|
||||
payload, err := json.Marshal(claims)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
body := base64.RawURLEncoding.EncodeToString(payload)
|
||||
sig := sign(body)
|
||||
return body + "." + sig, nil
|
||||
signingInput := header + "." + body
|
||||
sig := sign(signingInput, secret)
|
||||
return signingInput + "." + sig, nil
|
||||
}
|
||||
|
||||
// Parse 校验并解析 token。
|
||||
func Parse(token string) (*Claims, error) {
|
||||
parts := strings.SplitN(token, ".", 2)
|
||||
if len(parts) != 2 {
|
||||
return nil, errors.New("令牌格式错误")
|
||||
// Parse 校验并解析 token(签名不符返回 ErrInvalidToken,过期返回 ErrExpired)。
|
||||
func Parse(secret, token string) (*Claims, error) {
|
||||
parts := strings.Split(token, ".")
|
||||
if len(parts) != 3 {
|
||||
return nil, ErrInvalidToken
|
||||
}
|
||||
if sign(parts[0]) != parts[1] {
|
||||
return nil, errors.New("令牌签名校验失败")
|
||||
signingInput := parts[0] + "." + parts[1]
|
||||
if !hmac.Equal([]byte(sign(signingInput, secret)), []byte(parts[2])) {
|
||||
return nil, ErrInvalidToken
|
||||
}
|
||||
payload, err := base64.RawURLEncoding.DecodeString(parts[0])
|
||||
payload, err := base64.RawURLEncoding.DecodeString(parts[1])
|
||||
if err != nil {
|
||||
return nil, errors.New("令牌载荷解析失败")
|
||||
return nil, ErrInvalidToken
|
||||
}
|
||||
var c Claims
|
||||
if err = json.Unmarshal(payload, &c); err != nil {
|
||||
return nil, errors.New("令牌载荷解析失败")
|
||||
var claims Claims
|
||||
if err = json.Unmarshal(payload, &claims); err != nil {
|
||||
return nil, ErrInvalidToken
|
||||
}
|
||||
if c.Exp < time.Now().Unix() {
|
||||
return nil, errors.New("令牌已过期")
|
||||
if claims.Exp > 0 && time.Now().Unix() > claims.Exp {
|
||||
return nil, ErrExpired
|
||||
}
|
||||
return &c, nil
|
||||
return &claims, nil
|
||||
}
|
||||
|
||||
func sign(body string) string {
|
||||
mac := hmac.New(sha256.New, secret)
|
||||
mac.Write([]byte(body))
|
||||
func sign(input, secret string) string {
|
||||
mac := hmac.New(sha256.New, []byte(secret))
|
||||
mac.Write([]byte(input))
|
||||
return base64.RawURLEncoding.EncodeToString(mac.Sum(nil))
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user