我做的基礎服務項目,是如何實現 API 安全與限流的(短信、郵件、文件上傳、釘釘通知)
一、背景
最近我做了一個基礎服務項目,主要對外提供短信、郵件、文件上傳和釘釘通知等基礎功能。這些接口是多個業務系統都要調用的,所以安全性、穩定性、限流控制必須得做好。
這篇文章就記錄一下,我是怎么一步步設計這個系統,讓它既能安全對接多個項目,又能抗住高并發調用的。
二、我遇到了什么問題?
這個系統上線初期,其實就碰到了幾個問題:
- 接口被非法調用:沒有權限的系統也能調用我們的短信接口,導致誤發短信。
- 被刷接口:有個業務系統調用頻繁,導致我們服務被打崩,日志里全是超時。
- 多項目對接混亂:不同項目調用同一接口,但權限、限流、日志都不一樣,不好管理。
這些問題讓我意識到,必須從架構層面統一解決 API 安全、限流、多項目對接等問題。
三、我是怎么做的?
我最終設計了一套統一的安全和限流機制,結合 Redis、攔截器、Lua 腳本等技術,解決了這些問題。
1. API 接口安全設計:API Key + Redis 校驗
我給每個接入的項目分配一個唯一的 API Key
,所有請求都必須在 Header 中帶上 X-API-Key
。
Key 信息存在 Redis 中,包括:
- 租戶 ID(tenantId)
- 是否啟用
- 過期時間
每次請求進來,攔截器會先校驗 Key 是否有效。無效的直接返回 401。
實現亮點:
- 使用 Redis 存儲 Key,讀取快,不影響性能
- 攔截器統一處理,邏輯清晰
- 可結合租戶 ID 做日志追蹤、限流隔離等
示例攔截器代碼:
@Component
public class ApiKeyInterceptor implements HandlerInterceptor {@Autowiredprivate RedisTemplate<String, String> redisTemplate;@Overridepublic boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {String apiKey = request.getHeader("X-API-Key");if (StringUtils.isBlank(apiKey)) {response.sendError(HttpServletResponse.SC_UNAUTHORIZED, "Missing API Key");return false;}String value = redisTemplate.opsForValue().get("api_key:" + apiKey);if (value == null) {response.sendError(HttpServletResponse.SC_UNAUTHORIZED, "Invalid API Key");return false;}JSONObject json = JSON.parseObject(value);boolean enabled = json.getBoolean("enabled");long expireTime = json.getLong("expireTime");if (!enabled || System.currentTimeMillis() > expireTime) {response.sendError(HttpServletResponse.SC_UNAUTHORIZED, "API Key expired or disabled");return false;}return true;}
}
2. 限流設計:Redis + Lua 實現滑動窗口限流
一開始我用的是 Guava 的限流組件,但發現它只適用于單機。后來我換成了 Redis + Lua 腳本,實現了分布式的滑動窗口限流。
Lua 腳本:
local key = KEYS[1]
local limit = tonumber(ARGV[1])
local windowSize = tonumber(ARGV[2])
local now = tonumber(ARGV[3])redis.call('ZREMRANGEBYSCORE', key, 0, now - windowSize * 1000)
local count = redis.call('ZCARD', key)if count >= limit thenreturn false
elseredis.call('ZADD', key, now, now)return true
end
Java 調用邏輯:
@Service
public class RateLimitService {@Autowiredprivate RedisTemplate<String, String> redisTemplate;private static final Long LIMIT = 100L; // 每分鐘最多100次private static final Long WINDOW_SIZE = 60L; // 時間窗口60秒public boolean isAllowed(String apiKey) {String key = "rate_limit:" + apiKey;long now = System.currentTimeMillis();DefaultRedisScript<Boolean> script = new DefaultRedisScript<>(luaScript, Boolean.class);return redisTemplate.execute(script,Arrays.asList(key),LIMIT.toString(), WINDOW_SIZE.toString(), String.valueOf(now));}
}
然后我在攔截器中調用限流邏輯,拒絕超過限制的請求。
實現亮點:
- 支持分布式限流,適合多節點部署
- 滑動窗口比固定窗口更精準
- Lua 腳本保證原子性,避免并發問題
3. 多項目對接策略
為了支持多個項目接入,我做了以下幾點:
- 所有 API Key 綁定租戶 ID,便于日志追蹤、限流隔離
- 統一網關(Nginx + Spring Cloud Gateway)做路由、鑒權、限流
- 用 Nacos 管理 API Key、限流規則等配置,支持運行時熱更新
四、難點與亮點總結
難點:
-
如何在分布式環境下實現限流?
答案是 Redis + Lua 腳本,保證原子性,避免并發問題。 -
如何防止非法調用?
API Key + Redis 校驗機制,攔截器統一處理,簡單有效。 -
如何支持多項目對接?
給每個項目分配獨立的 API Key,綁定租戶信息,限流、日志、權限都按租戶隔離。
亮點:
- 模塊清晰,易于擴展:短信、郵件、文件上傳等功能模塊化,方便維護。
- 統一安全機制:API Key + 限流,保障系統安全。
- 支持高并發:Redis + Lua 腳本應對分布式限流,性能穩定。
- 可擴展性強:后續可接入 Nacos、Prometheus、日志系統等,形成完整生態。
五、效果如何?
這套方案在我們公司已經上線使用幾個月了,日均調用量幾萬次,沒出過安全問題,也沒被刷崩過,效果還不錯。
- 接口安全性大幅提升
- 限流機制有效防止了刷接口
- 多項目接入統一管理,效率提高
如果你也在做類似的平臺,或者想搭建一個對外的基礎服務系統,可以參考這套架構,親測可用。