fix: P0/P1 安全与质量缺陷修复 — 10 项 QA 审查问题解决
P0 安全修复: - tenant_rls: SQL 拼接改为参数化查询防止注入 - follow_up_service: UUID SQL 拼接改为参数化原生查询 - RLS 策略: 新迁移移除空字符串绕过条件 - SSE 消息推送: token 键名 'token' → 'access_token' 修复 - rate_limit: 登录端点 Redis 不可达时 fail-close P1 质量修复: - 小程序缓存清理: preservedKeys 补全认证键名 - 小程序 token 刷新: 失败时清除所有认证数据 - 小程序 401: redirectTo → reLaunch 兼容 tabBar - 集成测试: 信号量限制并行数据库创建(4个) - change_password: 乐观锁 version 硬编码 → 动态递增 测试: 516 全部通过 (含 153 集成测试)
This commit is contained in:
@@ -180,10 +180,13 @@ pub async fn account_lockout_middleware(
|
||||
) -> Response {
|
||||
let avail = redis_avail();
|
||||
|
||||
// Redis 不可达时 fail-open:放行请求
|
||||
// Redis 不可达时 fail-close:拒绝登录请求(安全优先)
|
||||
if !avail.should_try().await {
|
||||
tracing::warn!("Redis 不可达,fail-open 账户锁定检查放行");
|
||||
return next.run(req).await;
|
||||
tracing::error!("Redis 不可达,fail-close 拒绝登录请求");
|
||||
return (StatusCode::SERVICE_UNAVAILABLE, axum::Json(RateLimitResponse {
|
||||
error: "service_unavailable".to_string(),
|
||||
message: "安全服务暂不可用,请稍后重试".to_string(),
|
||||
})).into_response();
|
||||
}
|
||||
|
||||
// 获取 Redis 连接
|
||||
@@ -193,9 +196,12 @@ pub async fn account_lockout_middleware(
|
||||
c
|
||||
}
|
||||
Err(e) => {
|
||||
tracing::warn!(error = %e, "Redis 连接失败,fail-open 账户锁定检查放行");
|
||||
tracing::error!(error = %e, "Redis 连接失败,fail-close 拒绝登录请求");
|
||||
avail.mark_failed().await;
|
||||
return next.run(req).await;
|
||||
return (StatusCode::SERVICE_UNAVAILABLE, axum::Json(RateLimitResponse {
|
||||
error: "service_unavailable".to_string(),
|
||||
message: "安全服务暂不可用,请稍后重试".to_string(),
|
||||
})).into_response();
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -3,7 +3,7 @@ use axum::http::Request;
|
||||
use axum::middleware::Next;
|
||||
use axum::response::Response;
|
||||
use erp_core::types::TenantContext;
|
||||
use sea_orm::ConnectionTrait;
|
||||
use sea_orm::{ConnectionTrait, DatabaseBackend, Statement};
|
||||
|
||||
/// Tenant RLS 中间件。
|
||||
///
|
||||
@@ -21,11 +21,12 @@ pub async fn tenant_rls_middleware(
|
||||
let tenant_id = req.extensions().get::<TenantContext>().map(|ctx| ctx.tenant_id);
|
||||
|
||||
if let Some(tid) = tenant_id {
|
||||
// SET app.current_tenant_id — RLS 策略读取此值
|
||||
// SET app.current_tenant_id — RLS 策略读取此值(参数化查询防止注入)
|
||||
if let Err(e) = db
|
||||
.execute_unprepared(&format!(
|
||||
"SET app.current_tenant_id = '{}'",
|
||||
tid
|
||||
.execute(Statement::from_sql_and_values(
|
||||
DatabaseBackend::Postgres,
|
||||
"SET app.current_tenant_id = $1",
|
||||
[tid.into()],
|
||||
))
|
||||
.await
|
||||
{
|
||||
|
||||
Reference in New Issue
Block a user