Files
openteam/server/internal/pkg/jwt/jwt.go
T
Sakurasan 360c6b33a6 M0+M1: 基建 + 用户/密钥/核心代理
后端 (Go/Gin/GORM):
- 配置(viper+env)、SQLite/Postgres 迁移、argon2id、AES-GCM 渠道密钥、JWT+refresh cookie
- 用户注册/登录/刷新/登出、API Key CRUD(仅存哈希、明文一次展示)
- 代理网关: /v1/chat/completions、/v1/responses、/v1/models 直通 OpenAI 渠道
  非流式+流式(SSE 零缓冲转发), 用量捕获(chat 末块/responses completed 嵌套),
  OpenAI 错误格式(401/402/404/502), 余额检查
- 异步批量记账 + 余额流水 + 日聚合, admin 用户/余额/配置 API
- 单测: crypto/jwt/apikey/流式 usage 提取

前端 (Vue3+TS+Vite+Tailwind v4):
- taste-skill 设计 tokens: 深色仪表盘, 石墨+信号铜色, Outfit+JetBrains Mono
- Landing/登录/注册, 控制台(仪表盘图表/密钥管理/用量明细)
- 基础组件 Button/Input/Badge/Modal, ECharts 用量图

部署: docker-compose(nginx+api+postgres), 双 Dockerfile, nginx SSE 反代
联调: scripts/mockupstream 本地 mock 上游, 端到端验证通过
2026-08-15 13:10:47 +08:00

72 lines
1.8 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
// Package jwt 访问令牌(短时,存内存)与刷新令牌(HttpOnly Cookie)签发/校验。
package jwt
import (
"errors"
"time"
"github.com/golang-jwt/jwt/v5"
)
type Claims struct {
UserID uint64 `json:"uid"`
Username string `json:"uname"`
Role string `json:"role"`
jwt.RegisteredClaims
}
type Manager struct {
secret []byte
issuer string
accessTTL time.Duration
refreshTTL time.Duration
}
func NewManager(secret, issuer string, accessTTL, refreshTTL time.Duration) *Manager {
return &Manager{secret: []byte(secret), issuer: issuer, accessTTL: accessTTL, refreshTTL: refreshTTL}
}
func (m *Manager) AccessTTL() time.Duration { return m.accessTTL }
func (m *Manager) RefreshTTL() time.Duration { return m.refreshTTL }
// Sign 签发 token;typ 取 "access" / "refresh"。
func (m *Manager) Sign(userID uint64, username, role, typ string) (string, time.Time, error) {
ttl := m.accessTTL
if typ == "refresh" {
ttl = m.refreshTTL
}
now := time.Now()
exp := now.Add(ttl)
claims := Claims{
UserID: userID,
Username: username,
Role: role,
RegisteredClaims: jwt.RegisteredClaims{
Issuer: m.issuer,
Subject: typ,
IssuedAt: jwt.NewNumericDate(now),
ExpiresAt: jwt.NewNumericDate(exp),
},
}
tok := jwt.NewWithClaims(jwt.SigningMethodHS256, claims)
s, err := tok.SignedString(m.secret)
return s, exp, err
}
var ErrInvalidToken = errors.New("invalid token")
// Parse 校验签名与有效期。
func (m *Manager) Parse(token string) (*Claims, error) {
claims := &Claims{}
tok, err := jwt.ParseWithClaims(token, claims, func(t *jwt.Token) (any, error) {
if _, ok := t.Method.(*jwt.SigningMethodHMAC); !ok {
return nil, ErrInvalidToken
}
return m.secret, nil
})
if err != nil || !tok.Valid {
return nil, ErrInvalidToken
}
return claims, nil
}