feat(auth): upgrade casbin to v3 and enhance connection pool configurations
- Upgrade casbin from v2 to v3 across go.mod and pkg/auth/casbin.go - Add slide captcha verification to registration flow (CheckVerified, ConsumeVerified) - Add DB wrapper with connection pool statistics and health checks - Add Redis connection pool optimizations with stats and health monitoring - Add new config options: ConnMaxLifetime, HealthCheckInterval, EnableRetryOnError - Optimize slow query threshold from 200ms to 100ms - Add ping with retry mechanism for database and Redis connections
This commit is contained in:
@@ -41,9 +41,23 @@ func (h *AuthHandler) Register(c *gin.Context) {
|
||||
return
|
||||
}
|
||||
|
||||
// 验证滑动验证码(检查是否已验证)
|
||||
if ok, err := h.container.CaptchaService.CheckVerified(c.Request.Context(), req.CaptchaID); err != nil || !ok {
|
||||
h.logger.Warn("滑动验证码验证失败", zap.String("captcha_id", req.CaptchaID), zap.Error(err))
|
||||
RespondBadRequest(c, "滑动验证码验证失败", nil)
|
||||
return
|
||||
}
|
||||
|
||||
// 使用 defer 确保验证码在函数返回前被消耗(不管成功还是失败)
|
||||
defer func() {
|
||||
if err := h.container.CaptchaService.ConsumeVerified(c.Request.Context(), req.CaptchaID); err != nil {
|
||||
h.logger.Warn("消耗验证码失败", zap.String("captcha_id", req.CaptchaID), zap.Error(err))
|
||||
}
|
||||
}()
|
||||
|
||||
// 验证邮箱验证码
|
||||
if err := h.container.VerificationService.VerifyCode(c.Request.Context(), req.Email, req.VerificationCode, service.VerificationTypeRegister); err != nil {
|
||||
h.logger.Warn("验证码验证失败", zap.String("email", req.Email), zap.Error(err))
|
||||
h.logger.Warn("邮箱验证码验证失败", zap.String("email", req.Email), zap.Error(err))
|
||||
RespondBadRequest(c, err.Error(), nil)
|
||||
return
|
||||
}
|
||||
|
||||
@@ -86,8 +86,8 @@ func checkRedis(ctx context.Context) error {
|
||||
return errors.New("Redis客户端未初始化")
|
||||
}
|
||||
|
||||
// 使用Ping检查连接
|
||||
if err := client.Ping(ctx).Err(); err != nil {
|
||||
// 使用Ping检查连接(封装后的方法直接返回error)
|
||||
if err := client.Ping(ctx); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
|
||||
@@ -180,12 +180,50 @@ func (s *captchaService) Verify(ctx context.Context, dx int, captchaID string) (
|
||||
ty := redisData.Ty
|
||||
ok := slide.Validate(dx, ty, tx, ty, paddingValue)
|
||||
|
||||
// 验证后立即删除Redis记录(防止重复使用)
|
||||
// 验证成功后,标记为已验证状态,设置5分钟有效期
|
||||
if ok {
|
||||
verifiedKey := redisKeyPrefix + "verified:" + captchaID
|
||||
if err := s.redis.Set(ctx, verifiedKey, "1", 5*time.Minute); err != nil {
|
||||
s.logger.Warn("设置验证码已验证标记失败", zap.Error(err))
|
||||
}
|
||||
// 删除原始验证码记录(防止重复验证)
|
||||
if err := s.redis.Del(ctx, redisKey); err != nil {
|
||||
// 记录警告但不影响验证结果
|
||||
s.logger.Warn("删除验证码Redis记录失败", zap.Error(err))
|
||||
}
|
||||
}
|
||||
return ok, nil
|
||||
}
|
||||
|
||||
// CheckVerified 检查验证码是否已验证(仅检查captcha_id)
|
||||
func (s *captchaService) CheckVerified(ctx context.Context, captchaID string) (bool, error) {
|
||||
// 测试环境下直接通过验证
|
||||
cfg, err := config.GetConfig()
|
||||
if err == nil && cfg.IsTestEnvironment() {
|
||||
return true, nil
|
||||
}
|
||||
|
||||
verifiedKey := redisKeyPrefix + "verified:" + captchaID
|
||||
exists, err := s.redis.Exists(ctx, verifiedKey)
|
||||
if err != nil {
|
||||
return false, fmt.Errorf("检查验证状态失败: %w", err)
|
||||
}
|
||||
if exists == 0 {
|
||||
return false, errors.New("验证码未验证或已过期")
|
||||
}
|
||||
return true, nil
|
||||
}
|
||||
|
||||
// ConsumeVerified 消耗已验证的验证码(注册成功后调用)
|
||||
func (s *captchaService) ConsumeVerified(ctx context.Context, captchaID string) error {
|
||||
// 测试环境下直接返回成功
|
||||
cfg, err := config.GetConfig()
|
||||
if err == nil && cfg.IsTestEnvironment() {
|
||||
return nil
|
||||
}
|
||||
|
||||
verifiedKey := redisKeyPrefix + "verified:" + captchaID
|
||||
if err := s.redis.Del(ctx, verifiedKey); err != nil {
|
||||
s.logger.Warn("删除验证码已验证标记失败", zap.Error(err))
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -100,6 +100,8 @@ type VerificationService interface {
|
||||
type CaptchaService interface {
|
||||
Generate(ctx context.Context) (masterImg, tileImg, captchaID string, y int, err error)
|
||||
Verify(ctx context.Context, dx int, captchaID string) (bool, error)
|
||||
CheckVerified(ctx context.Context, captchaID string) (bool, error)
|
||||
ConsumeVerified(ctx context.Context, captchaID string) error
|
||||
}
|
||||
|
||||
// YggdrasilService Yggdrasil服务接口
|
||||
|
||||
@@ -330,10 +330,10 @@ func (s *textureService) UploadTexture(ctx context.Context, uploaderID int64, na
|
||||
}
|
||||
|
||||
// 生成对象名称(路径)
|
||||
// 格式: hash/{hash[:2]}/{hash[2:4]}/{hash}.png
|
||||
// 使用哈希值作为路径,避免重复存储相同文件
|
||||
// 格式: type/hash[:2]/hash
|
||||
// 使用哈希值作为文件名,不带扩展名
|
||||
textureTypeFolder := strings.ToLower(textureType)
|
||||
objectName := fmt.Sprintf("%s/%s/%s/%s/%s%s", textureTypeFolder, hash[:2], hash[2:4], hash, hash, ext)
|
||||
objectName := fmt.Sprintf("%s/%s", textureTypeFolder, hash)
|
||||
|
||||
// 上传文件
|
||||
reader := bytes.NewReader(fileData)
|
||||
|
||||
@@ -41,6 +41,7 @@ type RegisterRequest struct {
|
||||
Email string `json:"email" binding:"required,email" example:"user@example.com"`
|
||||
Password string `json:"password" binding:"required,min=6,max=128" example:"password123"`
|
||||
VerificationCode string `json:"verification_code" binding:"required,len=6" example:"123456"` // 邮箱验证码
|
||||
CaptchaID string `json:"captcha_id" binding:"required" example:"uuid-xxxx-xxxx"` // 滑动验证码ID
|
||||
Avatar string `json:"avatar" binding:"omitempty,url" example:"https://rustfs.example.com/avatars/user_1/avatar.png"` // 可选,用户自定义头像
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user