Nelze vybrat více než 25 témat Téma musí začínat písmenem nebo číslem, může obsahovat pomlčky („-“) a může být dlouhé až 35 znaků.
 
 
 

97 řádky
2.2 KiB

  1. package middleware
  2. import (
  3. "crypto/subtle"
  4. "net/http"
  5. "strings"
  6. "github.com/QuantumNous/new-api/setting/system_setting"
  7. "github.com/gin-gonic/gin"
  8. )
  9. // SyncAuth 同步 API 认证中间件
  10. // 通过 X-Sync-API-Key 请求头验证请求的合法性
  11. func SyncAuth() gin.HandlerFunc {
  12. return func(c *gin.Context) {
  13. settings := system_setting.GetRegionSyncSettings()
  14. // 如果未启用区域同步,拒绝请求
  15. if !settings.Enabled {
  16. c.JSON(http.StatusForbidden, gin.H{
  17. "success": false,
  18. "message": "region sync is not enabled",
  19. })
  20. c.Abort()
  21. return
  22. }
  23. // 检查 X-Sync-API-Key 请求头
  24. apiKey := c.GetHeader("X-Sync-API-Key")
  25. if apiKey == "" {
  26. c.JSON(http.StatusUnauthorized, gin.H{
  27. "success": false,
  28. "message": "missing sync API key",
  29. })
  30. c.Abort()
  31. return
  32. }
  33. // 验证 API Key
  34. if !validateSyncAPIKey(apiKey, settings.SyncApiKey) {
  35. c.JSON(http.StatusUnauthorized, gin.H{
  36. "success": false,
  37. "message": "invalid sync API key",
  38. })
  39. c.Abort()
  40. return
  41. }
  42. // 验证来源节点(从 X-Sync-Node 获取)
  43. node := c.GetHeader("X-Sync-Node")
  44. if node == "" {
  45. c.JSON(http.StatusBadRequest, gin.H{
  46. "success": false,
  47. "message": "missing sync node identifier",
  48. })
  49. c.Abort()
  50. return
  51. }
  52. // 将节点信息存储在上下文中
  53. c.Set("sync_node", node)
  54. c.Next()
  55. }
  56. }
  57. // validateSyncAPIKey 验证同步 API Key(使用常量时间比较防止 timing attack)
  58. func validateSyncAPIKey(requestedKey, configuredKey string) bool {
  59. if configuredKey == "" {
  60. return false
  61. }
  62. // 支持 master 配置多个 slave 的 key,以逗号分隔
  63. if strings.Contains(configuredKey, ",") {
  64. keys := strings.Split(configuredKey, ",")
  65. for _, key := range keys {
  66. if subtle.ConstantTimeCompare([]byte(strings.TrimSpace(key)), []byte(requestedKey)) == 1 {
  67. return true
  68. }
  69. }
  70. return false
  71. }
  72. return subtle.ConstantTimeCompare([]byte(strings.TrimSpace(configuredKey)), []byte(requestedKey)) == 1
  73. }
  74. // GetSyncNode 从上下文获取同步节点标识
  75. func GetSyncNode(c *gin.Context) string {
  76. if node, exists := c.Get("sync_node"); exists {
  77. if nodeStr, ok := node.(string); ok {
  78. return nodeStr
  79. }
  80. }
  81. return ""
  82. }