feat(worker-risk): 新增大厅阶梯式风控中间件与鉴权短缓存,惩罚外挂死循环狂刷并支持恶意封号
1. 新增 worker-risk-control 阶梯式风控:单打手/IP 限流 3次/秒(超限返 429);1 分钟累计违规 10 次升级为二级惩罚(锁定 5 分钟秒拒并带 Retry-After);惩罚期内持续暴力刷接口累计 30 次自动判定恶意不改,直接封禁打手账号(status='frozen')并作废所有 session; 2. 风控限流前置挂载在 requireWorkerSession 之前:命中限流或处于风控期时,微秒级在内存中基于 JWT payload 与 IP 拦截,彻底杜绝外挂流量穿透到 PostgreSQL 数据库; 3. verifyWorkerSessionToken 增加 3 秒内存校验缓存:并发请求直接复用内存鉴权对象,同一页面并发发出的多个接口请求仅查 1 次数据库,极大减轻连接池争抢压力; 4. 全套新增风控阶梯机制与自动封号自动化测试,后端 549 项测试全绿通过。
This commit is contained in:
@@ -41,6 +41,18 @@ export async function logoutWorkerSession(token: unknown) {
|
||||
}
|
||||
}
|
||||
|
||||
interface CachedVerifiedSession {
|
||||
session: WorkerSession
|
||||
expiresAtMs: number
|
||||
}
|
||||
|
||||
const verifiedSessionCache = new Map<string, CachedVerifiedSession>()
|
||||
const VERIFIED_SESSION_CACHE_TTL_MS = 3_000
|
||||
|
||||
export function clearVerifiedWorkerSessionCacheForTest(): void {
|
||||
verifiedSessionCache.clear()
|
||||
}
|
||||
|
||||
export async function verifyWorkerSessionToken(token: unknown): Promise<WorkerSession> {
|
||||
ensureWorkerAuthConfigured()
|
||||
const normalizedToken = String(token || '').trim()
|
||||
@@ -51,6 +63,13 @@ export async function verifyWorkerSessionToken(token: unknown): Promise<WorkerSe
|
||||
})
|
||||
}
|
||||
|
||||
// 命中 3 秒内存校验缓存:并发请求直接复用,避免同一页面连续发 3~5 个请求打爆数据库连接池
|
||||
const now = Date.now()
|
||||
const cached = verifiedSessionCache.get(normalizedToken)
|
||||
if (cached && cached.expiresAtMs > now) {
|
||||
return cached.session
|
||||
}
|
||||
|
||||
const parts = normalizedToken.split('.')
|
||||
const encodedPayload = parts[0]
|
||||
const signature = parts[1]
|
||||
@@ -123,7 +142,7 @@ export async function verifyWorkerSessionToken(token: unknown): Promise<WorkerSe
|
||||
})
|
||||
}
|
||||
|
||||
return {
|
||||
const verifiedSession: WorkerSession = {
|
||||
sessionId,
|
||||
workerId: Number(worker.id),
|
||||
username: worker.username,
|
||||
@@ -131,6 +150,23 @@ export async function verifyWorkerSessionToken(token: unknown): Promise<WorkerSe
|
||||
expiresAt,
|
||||
sessionVersion: currentSessionVersion,
|
||||
}
|
||||
|
||||
verifiedSessionCache.set(normalizedToken, {
|
||||
session: verifiedSession,
|
||||
expiresAtMs: now + VERIFIED_SESSION_CACHE_TTL_MS,
|
||||
})
|
||||
|
||||
// 控制缓存总数,防止内存泄漏
|
||||
if (verifiedSessionCache.size > 10_000) {
|
||||
const expiredNow = Date.now()
|
||||
for (const [key, item] of verifiedSessionCache.entries()) {
|
||||
if (item.expiresAtMs <= expiredNow) {
|
||||
verifiedSessionCache.delete(key)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return verifiedSession
|
||||
}
|
||||
|
||||
export async function getWorkerSessions(session: WorkerSession) {
|
||||
|
||||
Reference in New Issue
Block a user