fix(security): Q2 Chunk 2 — 多租户安全加固 + 限流 fail-closed
- auth_service::refresh() 添加 tenant_id 校验 - user_service get_by_id/update/delete/assign_roles 改为数据库级 tenant_id 过滤 - 限流中间件改为 fail-closed:Redis 不可达时返回 429 而非放行
This commit is contained in:
@@ -118,9 +118,14 @@ async fn apply_rate_limit(
|
||||
) -> Response {
|
||||
let avail = redis_avail();
|
||||
|
||||
// 快速跳过:Redis 不可达时直接放行
|
||||
// Redis 不可达时 fail-closed:拒绝请求
|
||||
if !avail.should_try().await {
|
||||
return next.run(req).await;
|
||||
tracing::warn!("Redis 不可达,启用 fail-closed 限流保护");
|
||||
let body = RateLimitResponse {
|
||||
error: "Too Many Requests".to_string(),
|
||||
message: "服务暂时不可用,请稍后重试".to_string(),
|
||||
};
|
||||
return (StatusCode::TOO_MANY_REQUESTS, axum::Json(body)).into_response();
|
||||
}
|
||||
|
||||
let key = format!("rate_limit:{}:{}", prefix, identifier);
|
||||
@@ -131,17 +136,25 @@ async fn apply_rate_limit(
|
||||
c
|
||||
}
|
||||
Err(e) => {
|
||||
tracing::warn!(error = %e, "Redis 连接失败,跳过限流");
|
||||
tracing::warn!(error = %e, "Redis 连接失败,fail-closed 限流保护");
|
||||
avail.mark_failed().await;
|
||||
return next.run(req).await;
|
||||
let body = RateLimitResponse {
|
||||
error: "Too Many Requests".to_string(),
|
||||
message: "服务暂时不可用,请稍后重试".to_string(),
|
||||
};
|
||||
return (StatusCode::TOO_MANY_REQUESTS, axum::Json(body)).into_response();
|
||||
}
|
||||
};
|
||||
|
||||
let count: i64 = match redis::cmd("INCR").arg(&key).query_async(&mut conn).await {
|
||||
Ok(n) => n,
|
||||
Err(e) => {
|
||||
tracing::warn!(error = %e, "Redis INCR 失败,跳过限流");
|
||||
return next.run(req).await;
|
||||
tracing::warn!(error = %e, "Redis INCR 失败,fail-closed 限流保护");
|
||||
let body = RateLimitResponse {
|
||||
error: "Too Many Requests".to_string(),
|
||||
message: "服务暂时不可用,请稍后重试".to_string(),
|
||||
};
|
||||
return (StatusCode::TOO_MANY_REQUESTS, axum::Json(body)).into_response();
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
Reference in New Issue
Block a user