turnstile_service.go 3.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112
  1. package service
  2. import (
  3. "context"
  4. "fmt"
  5. infraerrors "github.com/Wei-Shaw/sub2api/internal/pkg/errors"
  6. "github.com/Wei-Shaw/sub2api/internal/pkg/logger"
  7. )
  8. var (
  9. ErrTurnstileVerificationFailed = infraerrors.BadRequest("TURNSTILE_VERIFICATION_FAILED", "turnstile verification failed")
  10. ErrTurnstileNotConfigured = infraerrors.ServiceUnavailable("TURNSTILE_NOT_CONFIGURED", "turnstile not configured")
  11. ErrTurnstileInvalidSecretKey = infraerrors.BadRequest("TURNSTILE_INVALID_SECRET_KEY", "invalid turnstile secret key")
  12. )
  13. // TurnstileVerifier 验证 Turnstile token 的接口
  14. type TurnstileVerifier interface {
  15. VerifyToken(ctx context.Context, secretKey, token, remoteIP string) (*TurnstileVerifyResponse, error)
  16. }
  17. // TurnstileService Turnstile 验证服务
  18. type TurnstileService struct {
  19. settingService *SettingService
  20. verifier TurnstileVerifier
  21. }
  22. // TurnstileVerifyResponse Cloudflare Turnstile 验证响应
  23. type TurnstileVerifyResponse struct {
  24. Success bool `json:"success"`
  25. ChallengeTS string `json:"challenge_ts"`
  26. Hostname string `json:"hostname"`
  27. ErrorCodes []string `json:"error-codes"`
  28. Action string `json:"action"`
  29. CData string `json:"cdata"`
  30. }
  31. // NewTurnstileService 创建 Turnstile 服务实例
  32. func NewTurnstileService(settingService *SettingService, verifier TurnstileVerifier) *TurnstileService {
  33. return &TurnstileService{
  34. settingService: settingService,
  35. verifier: verifier,
  36. }
  37. }
  38. // VerifyToken 验证 Turnstile token
  39. func (s *TurnstileService) VerifyToken(ctx context.Context, token string, remoteIP string) error {
  40. // 检查是否启用 Turnstile
  41. if !s.settingService.IsTurnstileEnabled(ctx) {
  42. logger.LegacyPrintf("service.turnstile", "%s", "[Turnstile] Disabled, skipping verification")
  43. return nil
  44. }
  45. // 获取 Secret Key
  46. secretKey := s.settingService.GetTurnstileSecretKey(ctx)
  47. return s.VerifyTokenWithSecret(ctx, secretKey, token, remoteIP)
  48. }
  49. func (s *TurnstileService) VerifyTokenWithSecret(ctx context.Context, secretKey, token, remoteIP string) error {
  50. if secretKey == "" {
  51. logger.LegacyPrintf("service.turnstile", "%s", "[Turnstile] Secret key not configured")
  52. return ErrTurnstileNotConfigured
  53. }
  54. // 如果 token 为空,返回错误
  55. if token == "" {
  56. logger.LegacyPrintf("service.turnstile", "%s", "[Turnstile] Token is empty")
  57. return ErrTurnstileVerificationFailed
  58. }
  59. logger.LegacyPrintf("service.turnstile", "[Turnstile] Verifying token for IP: %s", remoteIP)
  60. if s == nil || s.verifier == nil {
  61. return ErrTurnstileNotConfigured
  62. }
  63. result, err := s.verifier.VerifyToken(ctx, secretKey, token, remoteIP)
  64. if err != nil {
  65. logger.LegacyPrintf("service.turnstile", "[Turnstile] Request failed: %v", err)
  66. return fmt.Errorf("send request: %w", err)
  67. }
  68. if !result.Success {
  69. logger.LegacyPrintf("service.turnstile", "[Turnstile] Verification failed, error codes: %v", result.ErrorCodes)
  70. return ErrTurnstileVerificationFailed
  71. }
  72. logger.LegacyPrintf("service.turnstile", "%s", "[Turnstile] Verification successful")
  73. return nil
  74. }
  75. // IsEnabled 检查 Turnstile 是否启用
  76. func (s *TurnstileService) IsEnabled(ctx context.Context) bool {
  77. return s.settingService.IsTurnstileEnabled(ctx)
  78. }
  79. // ValidateSecretKey 验证 Turnstile Secret Key 是否有效
  80. func (s *TurnstileService) ValidateSecretKey(ctx context.Context, secretKey string) error {
  81. // 发送一个测试token的验证请求来检查secret_key是否有效
  82. result, err := s.verifier.VerifyToken(ctx, secretKey, "test-validation", "")
  83. if err != nil {
  84. return fmt.Errorf("validate secret key: %w", err)
  85. }
  86. // 检查是否有 invalid-input-secret 错误
  87. for _, code := range result.ErrorCodes {
  88. if code == "invalid-input-secret" {
  89. return ErrTurnstileInvalidSecretKey
  90. }
  91. }
  92. // 其他错误(如 invalid-input-response)说明 secret key 是有效的
  93. return nil
  94. }