package auth import ( "errors" "sync" "time" "ws_usernode/internal/pkg" ) // ErrCaptchaInvalid 表示图形验证码校验失败。 var ErrCaptchaInvalid = errors.New("auth: 图形验证码错误") // Captcha 图形验证码(防机器人,登录前置)。 type Captcha struct { ID string Text string // M1 生成图像渲染,此处仅存文本 } // CaptchaStore 为图形验证码存储(M1 实现图像渲染)。 type CaptchaStore interface { // New 生成一个验证码并返回其 ID。 New() (*Captcha, error) // Verify 校验并一次性消费。失败或过期返回 false。 Verify(id, answer string) bool } // MemoryCaptchaStore 单实例内存实现。 type MemoryCaptchaStore struct { mu sync.Mutex entries map[string]*captchaEntry } type captchaEntry struct { text string expiresAt time.Time } // NewMemoryCaptchaStore 创建内存图形验证码存储。 func NewMemoryCaptchaStore() *MemoryCaptchaStore { return &MemoryCaptchaStore{entries: make(map[string]*captchaEntry)} } func (s *MemoryCaptchaStore) New() (*Captcha, error) { text, err := pkg.RandomDigits(4) if err != nil { return nil, err } id, err := pkg.RandomHex(16) if err != nil { return nil, err } s.mu.Lock() defer s.mu.Unlock() s.entries[id] = &captchaEntry{text: text, expiresAt: time.Now().Add(5 * time.Minute)} return &Captcha{ID: id, Text: text}, nil } func (s *MemoryCaptchaStore) Verify(id, answer string) bool { s.mu.Lock() defer s.mu.Unlock() e, ok := s.entries[id] if !ok { return false } delete(s.entries, id) // 一次性 return e.text == answer && time.Now().Before(e.expiresAt) }