Files
Yunling Zhang 78673558c8 feat(core): Go 模块与账号、认证、用量基础包
- account: accounts.json 原子读写、常量时间口令校验、增删改查与用户名校验
- auth: HMAC 会话签名 + 管理员 bcrypt 凭据初始化
- randstr: 口令生成;usage: 目录用量统计
- 单测覆盖口令对错、账号禁用、只读、路径逃逸、持久化与 0600 权限
2026-09-28 16:52:33 +08:00

67 lines
1.6 KiB
Go

// Package auth 提供会话 cookie 的签名/校验,以及管理员凭据的初始化。
package auth
import (
"crypto/hmac"
"crypto/sha256"
"encoding/base64"
"encoding/json"
"strings"
"time"
)
// Session 是 cookie 里承载的最小信息。
type Session struct {
Kind string `json:"k"`
User string `json:"u,omitempty"`
Exp int64 `json:"e"`
}
// Signer 用 HMAC-SHA256 对会话载荷签名。
type Signer struct {
key []byte
}
// NewSigner 用 secret.key 构造签名器。
func NewSigner(key []byte) *Signer {
return &Signer{key: key}
}
// Issue 生成 kind/user 对应的 cookie 值,ttl 之后过期。
func (s *Signer) Issue(kind, user string, ttl time.Duration) string {
sess := Session{Kind: kind, User: user, Exp: time.Now().Add(ttl).Unix()}
payload, _ := json.Marshal(sess)
body := base64.RawURLEncoding.EncodeToString(payload)
return body + "." + s.sign(body)
}
func (s *Signer) sign(body string) string {
m := hmac.New(sha256.New, s.key)
m.Write([]byte(body))
return base64.RawURLEncoding.EncodeToString(m.Sum(nil))
}
// Verify 校验签名与有效期,返回会话内容。
func (s *Signer) Verify(token string) (Session, bool) {
i := strings.IndexByte(token, '.')
if i < 0 {
return Session{}, false
}
body, sig := token[:i], token[i+1:]
if !hmac.Equal([]byte(sig), []byte(s.sign(body))) {
return Session{}, false
}
payload, err := base64.RawURLEncoding.DecodeString(body)
if err != nil {
return Session{}, false
}
var sess Session
if err := json.Unmarshal(payload, &sess); err != nil {
return Session{}, false
}
if sess.Exp < time.Now().Unix() {
return Session{}, false
}
return sess, true
}