package common import ( "crypto/rand" "math/big" "strings" "sync" "time" "github.com/google/uuid" ) type verificationValue struct { code string time time.Time } const ( EmailVerificationPurpose = "v" PasswordResetPurpose = "r" PasswordResetEmailCodePurpose = "rec" // 忘记密码:邮箱 6 位数字验证码(与链接重置 token 区分) ) var verificationMutex sync.Mutex var verificationMap map[string]verificationValue var verificationMapMaxSize = 10 var VerificationValidMinutes = 10 func GenerateVerificationCode(length int) string { code := uuid.New().String() code = strings.Replace(code, "-", "", -1) if length == 0 { return code } return code[:length] } // GenerateNumericVerificationCode 生成指定长度的纯数字验证码(用于短信数字模板)。 func GenerateNumericVerificationCode(length int) string { if length <= 0 { length = 6 } digits := make([]byte, length) for i := 0; i < length; i++ { n, err := rand.Int(rand.Reader, big.NewInt(10)) if err != nil { // 极端情况下兜底,保证返回数字字符。 digits[i] = '0' continue } digits[i] = byte('0' + n.Int64()) } return string(digits) } func RegisterVerificationCodeWithKey(key string, code string, purpose string) { verificationMutex.Lock() defer verificationMutex.Unlock() verificationMap[purpose+key] = verificationValue{ code: code, time: time.Now(), } if len(verificationMap) > verificationMapMaxSize { removeExpiredPairs() } } func VerifyCodeWithKey(key string, code string, purpose string) bool { verificationMutex.Lock() defer verificationMutex.Unlock() value, okay := verificationMap[purpose+key] now := time.Now() if !okay || int(now.Sub(value.time).Seconds()) >= VerificationValidMinutes*60 { return false } return code == value.code } func DeleteKey(key string, purpose string) { verificationMutex.Lock() defer verificationMutex.Unlock() delete(verificationMap, purpose+key) } // no lock inside, so the caller must lock the verificationMap before calling! func removeExpiredPairs() { now := time.Now() for key := range verificationMap { if int(now.Sub(verificationMap[key].time).Seconds()) >= VerificationValidMinutes*60 { delete(verificationMap, key) } } } func init() { verificationMutex.Lock() defer verificationMutex.Unlock() verificationMap = make(map[string]verificationValue) }