diff --git a/crates/erp-auth/Cargo.toml b/crates/erp-auth/Cargo.toml index fd90484..4db2620 100644 --- a/crates/erp-auth/Cargo.toml +++ b/crates/erp-auth/Cargo.toml @@ -26,3 +26,4 @@ cbc.workspace = true hex.workspace = true base64 = "0.22" redis.workspace = true +dashmap.workspace = true diff --git a/crates/erp-auth/src/middleware/jwt_auth.rs b/crates/erp-auth/src/middleware/jwt_auth.rs index 9c3f3a4..caa65cc 100644 --- a/crates/erp-auth/src/middleware/jwt_auth.rs +++ b/crates/erp-auth/src/middleware/jwt_auth.rs @@ -2,6 +2,7 @@ use axum::body::Body; use axum::http::Request; use axum::middleware::Next; use axum::response::Response; +use dashmap::DashMap; use erp_core::error::AppError; use erp_core::request_info::REQUEST_INFO; use erp_core::request_info::RequestInfo; @@ -12,11 +13,11 @@ use crate::service::token_service::TokenService; type DeptIds = Vec; type DataScopes = std::collections::HashMap; type ScopeCacheEntry = (DeptIds, DataScopes, std::time::Instant); -type ScopeCacheMap = std::collections::HashMap; /// 用户权限数据缓存(user_id -> (department_ids, data_scopes, cached_at)) -static USER_SCOPE_CACHE: std::sync::LazyLock> = - std::sync::LazyLock::new(|| std::sync::RwLock::new(std::collections::HashMap::new())); +/// DashMap 分片并发,读写无锁竞争 +static USER_SCOPE_CACHE: std::sync::LazyLock> = + std::sync::LazyLock::new(DashMap::new); const SCOPE_CACHE_TTL: std::time::Duration = std::time::Duration::from_secs(60); @@ -76,16 +77,17 @@ pub async fn jwt_auth_middleware_fn( } // 查询用户所属部门 ID 列表 + 权限数据范围(带 60 秒缓存) - let cached = { - let cache = USER_SCOPE_CACHE.read().unwrap(); - cache.get(&claims.sub).and_then(|(depts, scopes, at)| { - if at.elapsed() < SCOPE_CACHE_TTL { - Some((depts.clone(), scopes.clone())) - } else { - None - } - }) - }; + let cached = USER_SCOPE_CACHE.get(&claims.sub).and_then(|entry| { + let (_, _, at) = entry.value(); + if at.elapsed() < SCOPE_CACHE_TTL { + let (depts, scopes, _) = entry.value(); + Some((depts.clone(), scopes.clone())) + } else { + drop(entry); + USER_SCOPE_CACHE.remove(&claims.sub); + None + } + }); let (department_ids, permission_data_scopes) = match cached { Some(hit) => hit, None => fetch_and_cache_scopes(claims.sub, claims.tid, &db).await, @@ -207,15 +209,14 @@ async fn fetch_and_cache_scopes( Some(conn) => fetch_permission_data_scopes(user_id, tenant_id, conn).await, None => std::collections::HashMap::new(), }; - let mut cache = USER_SCOPE_CACHE.write().unwrap(); - cache.insert( + USER_SCOPE_CACHE.insert( user_id, (depts.clone(), scopes.clone(), std::time::Instant::now()), ); - // 惰性淘汰过期条目,防止 HashMap 无限增长 - if cache.len() > 500 { + // 惰性淘汰过期条目,防止 DashMap 无限增长 + if USER_SCOPE_CACHE.len() > 500 { let now = std::time::Instant::now(); - cache.retain(|_, (_, _, at)| now.duration_since(*at) < SCOPE_CACHE_TTL); + USER_SCOPE_CACHE.retain(|_, (_, _, at)| now.duration_since(*at) < SCOPE_CACHE_TTL); } (depts, scopes) }