diff --git a/.gitignore b/.gitignore
index 8631db7..568fcee 100644
--- a/.gitignore
+++ b/.gitignore
@@ -437,3 +437,4 @@ MigrationBackup/
# BitFun snapshot data - auto managed
.bitfun/
+/.bitfun/search/flashgrep-index/
diff --git a/Configurations/SimApiOptions.cs b/Configurations/SimApiOptions.cs
index c3641bd..cc7136f 100644
--- a/Configurations/SimApiOptions.cs
+++ b/Configurations/SimApiOptions.cs
@@ -17,6 +17,12 @@ public class SimApiOptions
///
public bool EnableSimApiAuth { get; set; }
+ ///
+ /// 启用Cache功能
+ ///
+ public bool EnableSimApiCache { get; set; } = true;
+
+
///
/// 启用SimApi网关授权, 基于上层网关透传的身份令牌验证
///
diff --git a/Helpers/SimApiAuth.cs b/Helpers/SimApiAuth.cs
index 46a8f9d..dc78ced 100644
--- a/Helpers/SimApiAuth.cs
+++ b/Helpers/SimApiAuth.cs
@@ -1,19 +1,32 @@
using System;
+using System.Collections.Concurrent;
using System.Collections.Generic;
using Microsoft.Extensions.Caching.Distributed;
+using Microsoft.Extensions.DependencyInjection;
using SimApi.Communications;
using StackExchange.Redis;
namespace SimApi.Helpers;
///
-/// 认证助手
+/// 认证助手(支持 Redis 和 InMemory 两种模式)
///
-public class SimApiAuth(IDistributedCache cache, IConnectionMultiplexer redis)
+public class SimApiAuth
{
private const string TokenCacheKey = "SimApi:Auth:Token:{token}";
private const string TokenSetCacheKey = "SimApi:Auth:User:{userId}";
- private readonly IDatabase _redisDb = redis.GetDatabase();
+
+ private readonly IDistributedCache _cache;
+ private readonly IDatabase? _redisDb;
+
+ // InMemory 模式:用户 → Token集合
+ private readonly ConcurrentDictionary> _userTokens = new();
+
+ public SimApiAuth(IDistributedCache cache, IServiceProvider sp)
+ {
+ _cache = cache;
+ _redisDb = sp.GetService()?.GetDatabase();
+ }
///
/// 登录信息
@@ -28,13 +41,20 @@ public class SimApiAuth(IDistributedCache cache, IConnectionMultiplexer redis)
token ??= Guid.NewGuid().ToString();
var cacheKey = TokenCacheKey.Replace("{token}", token);
var setCacheKey = TokenSetCacheKey.Replace("{userId}", loginItem.Id);
- _redisDb.SetAdd(setCacheKey, token);
- cache.SetString(cacheKey, SimApiUtil.Json(loginItem),
- new DistributedCacheEntryOptions
- {
- SlidingExpiration = expireTime
- });
- _redisDb.KeyExpire(setCacheKey, expireTime.Value);
+
+ _cache.SetString(cacheKey, SimApiUtil.Json(loginItem),
+ new DistributedCacheEntryOptions { SlidingExpiration = expireTime });
+
+ if (_redisDb != null)
+ {
+ _redisDb.SetAdd(setCacheKey, token);
+ _redisDb.KeyExpire(setCacheKey, expireTime.Value);
+ }
+ else
+ {
+ var tokens = _userTokens.GetOrAdd(loginItem.Id, _ => new ConcurrentDictionary());
+ tokens.TryAdd(token, 0);
+ }
return token;
}
@@ -48,10 +68,15 @@ public class SimApiAuth(IDistributedCache cache, IConnectionMultiplexer redis)
public string Update(SimApiLoginItem loginItem, string token)
{
var cacheKey = TokenCacheKey.Replace("{token}", token);
- cache.SetString(cacheKey, SimApiUtil.Json(loginItem));
- var ttl = _redisDb.KeyTimeToLive(cacheKey);
- var setCacheKey = TokenSetCacheKey.Replace("{userId}", loginItem.Id);
- _redisDb.KeyExpire(setCacheKey, ttl);
+ _cache.SetString(cacheKey, SimApiUtil.Json(loginItem));
+
+ if (_redisDb != null)
+ {
+ var ttl = _redisDb.KeyTimeToLive(cacheKey);
+ var setCacheKey = TokenSetCacheKey.Replace("{userId}", loginItem.Id);
+ _redisDb.KeyExpire(setCacheKey, ttl);
+ }
+
return token;
}
@@ -64,9 +89,10 @@ public class SimApiAuth(IDistributedCache cache, IConnectionMultiplexer redis)
public SimApiLoginItem? GetLogin(string token)
{
var cacheKey = TokenCacheKey.Replace("{token}", token);
- var login = cache.GetString(cacheKey);
+ var login = _cache.GetString(cacheKey);
var resp = login != null ? SimApiUtil.FromJson(login) : null;
- if (resp != null)
+
+ if (resp != null && _redisDb != null)
{
var ttl = _redisDb.KeyTimeToLive(cacheKey);
var setCacheKey = TokenSetCacheKey.Replace("{userId}", resp.Id);
@@ -83,26 +109,42 @@ public class SimApiAuth(IDistributedCache cache, IConnectionMultiplexer redis)
///
public SimApiLoginItem[] GetAllLogins(string userId)
{
- var setCacheKey = TokenSetCacheKey.Replace("{userId}", userId);
- var allLogins = _redisDb.SetMembers(setCacheKey).ToStringArray();
- var resp = new List();
- foreach (var login in allLogins)
+ if (_redisDb != null)
{
- if (login != null)
+ var setCacheKey = TokenSetCacheKey.Replace("{userId}", userId);
+ var allLogins = _redisDb.SetMembers(setCacheKey).ToStringArray();
+ var resp = new List();
+ foreach (var login in allLogins)
{
- var item = GetLogin(login);
- if (item != null)
+ if (login != null)
{
- resp.Add(item);
- }
- else
- {
- _redisDb.SetRemove(setCacheKey, login);
+ var item = GetLogin(login);
+ if (item != null)
+ resp.Add(item);
+ else
+ _redisDb.SetRemove(setCacheKey, login);
}
}
- }
- return resp.ToArray();
+ return resp.ToArray();
+ }
+ else
+ {
+ if (!_userTokens.TryGetValue(userId, out var tokens))
+ return [];
+
+ var resp = new List();
+ foreach (var token in tokens.Keys)
+ {
+ var item = GetLogin(token);
+ if (item != null)
+ resp.Add(item);
+ else
+ tokens.TryRemove(token, out _);
+ }
+
+ return resp.ToArray();
+ }
}
///
@@ -111,18 +153,32 @@ public class SimApiAuth(IDistributedCache cache, IConnectionMultiplexer redis)
///
public void LogoutAll(string userId)
{
- var setCacheKey = TokenSetCacheKey.Replace("{userId}", userId);
- var allLogins = _redisDb.SetMembers(setCacheKey).ToStringArray();
- foreach (var login in allLogins)
+ if (_redisDb != null)
{
- if (login != null)
+ var setCacheKey = TokenSetCacheKey.Replace("{userId}", userId);
+ var allLogins = _redisDb.SetMembers(setCacheKey).ToStringArray();
+ foreach (var login in allLogins)
{
- var cacheKey = TokenCacheKey.Replace("{token}", login);
- cache.Remove(cacheKey);
+ if (login != null)
+ {
+ var cacheKey = TokenCacheKey.Replace("{token}", login);
+ _cache.Remove(cacheKey);
+ }
+ }
+
+ _redisDb.KeyDelete(setCacheKey);
+ }
+ else
+ {
+ if (_userTokens.TryRemove(userId, out var tokens))
+ {
+ foreach (var token in tokens.Keys)
+ {
+ var cacheKey = TokenCacheKey.Replace("{token}", token);
+ _cache.Remove(cacheKey);
+ }
}
}
-
- _redisDb.KeyDelete(setCacheKey);
}
///
@@ -132,13 +188,22 @@ public class SimApiAuth(IDistributedCache cache, IConnectionMultiplexer redis)
public void Logout(string token)
{
var item = GetLogin(token);
- if (item != null)
+
+ if (_redisDb != null)
{
- var setCacheKey = TokenSetCacheKey.Replace("{userId}", item.Id);
- _redisDb.SetRemove(setCacheKey, token);
+ if (item != null)
+ {
+ var setCacheKey = TokenSetCacheKey.Replace("{userId}", item.Id);
+ _redisDb.SetRemove(setCacheKey, token);
+ }
+ }
+ else
+ {
+ if (item != null && _userTokens.TryGetValue(item.Id, out var tokens))
+ tokens.TryRemove(token, out _);
}
var cacheKey = TokenCacheKey.Replace("{token}", token);
- cache.Remove(cacheKey);
+ _cache.Remove(cacheKey);
}
-}
\ No newline at end of file
+}
diff --git a/README.md b/README.md
index 1103ba6..5c87bb7 100644
--- a/README.md
+++ b/README.md
@@ -13,6 +13,7 @@ ASP.NET Core API 基础框架库,提供统一异常拦截、响应封装、Tok
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddSimApi(options =>
{
+ // RedisConfiguration 可选:配置则使用 Redis,不配则自动使用 InMemory
options.RedisConfiguration = "localhost:6379";
options.EnableSimApiAuth = true;
options.EnableSimApiDoc = true;
@@ -191,6 +192,26 @@ public class SimApiLoginItem {
**Token 传参**: Header `Token: `
+### 存储模式
+
+`SimApiAuth` 内部自动判断,无需手动配置:
+
+| 条件 | 存储后端 | 用户↔Token 映射 |
+|------|---------|-----------------|
+| 配置了 `RedisConfiguration` | Redis(`IDistributedCache` + Set) | Redis Set |
+| 未配置 `RedisConfiguration` | InMemory(`DistributedMemoryCache`) | `ConcurrentDictionary` |
+
+- **Redis 模式**:支持多实例共享,Token 持久化,适合生产环境
+- **InMemory 模式**:零配置即可启用 `EnableSimApiAuth`,适合开发/测试/单实例场景。注意重启后所有登录态丢失
+
+```csharp
+// 不配 Redis 也能用 Auth
+builder.Services.AddSimApi(options =>
+{
+ options.EnableSimApiAuth = true; // 自动使用 InMemory
+});
+```
+
### 认证后处理 Hook — ISimApiAuthChecker
```csharp
@@ -406,9 +427,11 @@ IMinioClient Client { get; } // 底层 MinIO 客户端
---
-## 8. Redis 缓存 — SimApiCache
+## 8. 缓存 — SimApiCache
-依赖 `RedisConfiguration`,Key 自动加前缀 `SimApi:Cache:`。
+通过 `EnableSimApiCache`(默认 `true`)控制。Key 自动加前缀 `SimApi:Cache:`。
+
+存储后端与 `SimApiAuth` 一致:配了 `RedisConfiguration` 就用 Redis,否则用 InMemory。
```csharp
void Set(string key, object value, DistributedCacheEntryOptions? options = null);
@@ -617,6 +640,7 @@ builder.Services.AddSimApi(options =>
// 功能开关
options.EnableSimApiAuth = false; // Token 认证
+ options.EnableSimApiCache = true; // 缓存(Redis 或 InMemory)
options.EnableSimApiAuthGate = false; // Auth Center 网关鉴权
options.EnableSimApiDoc = false; // Swagger 文档
options.EnableSimApiStorage = false; // S3 存储
diff --git a/SimApiExtensions.cs b/SimApiExtensions.cs
index 9a220dd..8008ae2 100644
--- a/SimApiExtensions.cs
+++ b/SimApiExtensions.cs
@@ -36,11 +36,20 @@ public static class SimApiExtensions
{
var simApiOptions = new SimApiOptions();
options?.Invoke(simApiOptions);
+
if (simApiOptions.RedisConfiguration != null)
{
builder.AddStackExchangeRedisCache(x => x.Configuration = simApiOptions.RedisConfiguration);
builder.AddSingleton(_ =>
ConnectionMultiplexer.Connect(simApiOptions.RedisConfiguration));
+ }
+ else if (simApiOptions.EnableSimApiAuth || simApiOptions.EnableSimApiCache)
+ {
+ builder.AddDistributedMemoryCache();
+ }
+
+ if (simApiOptions.EnableSimApiCache)
+ {
builder.AddSingleton();
}
@@ -360,6 +369,11 @@ public static class SimApiExtensions
logger.LogInformation("开始配置 RedisCache ...");
}
+ if (options.EnableSimApiCache)
+ {
+ logger.LogInformation("开始配置SimApiCache...");
+ }
+
//请求一下检测存储错误
if (options.EnableSimApiStorage)
{