fix: 修复登录重复创建会话 + 备注输入框不可交互

- login.js 增加防重复提交锁,请求期间禁用按钮
- Manager.Create 增加幂等检查,同用户同IP同UA 10秒内复用已有会话
- 移除当前设备备注输入框的 readonly 限制,改为 data-current 标记
- 修复 readonly 状态下输入框完全透明无法点击的问题
This commit is contained in:
2026-05-31 00:28:00 +08:00
parent 47ef3f2a99
commit d5d33c3ace
4 changed files with 68 additions and 14 deletions

View File

@ -2,6 +2,7 @@ package session
import (
"log"
"sync"
"time"
"metazone.cc/metalab/internal/config"
@ -15,18 +16,46 @@ type userStore interface {
// Manager 会话管理器:创建、验证、销毁、续期
type Manager struct {
store Store
userRepo userStore
cfg *config.Config
store Store
userRepo userStore
cfg *config.Config
mu sync.Mutex
recentLogin map[uint]*loginEntry // uid → 最近一次登录记录(防重复创建)
}
type loginEntry struct {
sid string
ip string
userAgent string
createdAt time.Time
}
// NewManager 构造函数
func NewManager(store Store, userRepo userStore, cfg *config.Config) *Manager {
return &Manager{store: store, userRepo: userRepo, cfg: cfg}
return &Manager{
store: store,
userRepo: userRepo,
cfg: cfg,
recentLogin: make(map[uint]*loginEntry),
}
}
// Create 创建会话并写入存储
// Create 创建会话并写入存储(同用户+同 IP+同 UA 在 10 秒内重复调用时返回已有会话,防重复创建)
func (m *Manager) Create(user *model.User, rememberMe bool, ip, userAgent string) (string, error) {
m.mu.Lock()
// 幂等检查:同用户+同 IP+同 UA10 秒内不重复创建
if entry, ok := m.recentLogin[user.ID]; ok {
if entry.ip == ip && entry.userAgent == userAgent && time.Since(entry.createdAt) < 10*time.Second {
// 验证旧会话仍然有效
if s, _ := m.store.Get(entry.sid); s != nil {
m.mu.Unlock()
log.Printf("[SessionManager] Create dedup uid=%d reuse sid=%s", user.ID, entry.sid[:16]+"...")
return entry.sid, nil
}
}
}
m.mu.Unlock()
sid, err := NewID()
if err != nil {
return "", err
@ -47,6 +76,11 @@ func (m *Manager) Create(user *model.User, rememberMe bool, ip, userAgent string
if err := m.store.Set(s); err != nil {
return "", err
}
m.mu.Lock()
m.recentLogin[user.ID] = &loginEntry{sid: sid, ip: ip, userAgent: userAgent, createdAt: now}
m.mu.Unlock()
log.Printf("[SessionManager] Created sid=%s uid=%d rememberMe=%v ip=%s", sid[:16]+"...", user.ID, rememberMe, ip)
return sid, nil
}