diff --git a/src/CloudGaming/Api/CloudGaming.Api/Program.cs b/src/CloudGaming/Api/CloudGaming.Api/Program.cs
index 0bbf42c..482d7a1 100644
--- a/src/CloudGaming/Api/CloudGaming.Api/Program.cs
+++ b/src/CloudGaming/Api/CloudGaming.Api/Program.cs
@@ -151,6 +151,7 @@ var app = builder.Build();
// app.UseSwagger();
// app.UseSwaggerUI();
//}
+
app.UseSwagger();
app.UseSwaggerUI(c =>
{
@@ -165,6 +166,7 @@ app.UseSwaggerUI(c =>
app.UseHttpsRedirection();
+//注册身份验证中间件
app.UseAuthorization();
//数据库中间件
app.UseMultiTenant();
diff --git a/src/CloudGaming/Code/CloudGaming.Code/Account/AccountBLL.cs b/src/CloudGaming/Code/CloudGaming.Code/Account/AccountBLL.cs
index 8344312..0688d43 100644
--- a/src/CloudGaming/Code/CloudGaming.Code/Account/AccountBLL.cs
+++ b/src/CloudGaming/Code/CloudGaming.Code/Account/AccountBLL.cs
@@ -126,13 +126,11 @@ namespace CloudGaming.Code.Account
Token = jwt.AccessToken,
UserId = user.Id,
};
- //创建设备号
- var dev = await ManageUserDevicesAsync(user, account, jwt.AccessToken);
- var key = $"user:login:{user.Id}";
+ //获取登录的设备
+ var (deviceList, currentDevice) = await ManageUserDevicesAsync(user, account, jwt.AccessToken);
+ // 管理设备和Redis缓存
+ await ManageDeviceCacheAsync(user.Id, currentDevice.TokenMd5, deviceList);
- //创建redis缓存
- await RedisCache.StringSetAsync(key, $"1", TimeSpan.FromHours(1));
- //accountUserLoginInfos.Add(new AccountUserLoginInfo());
T_User_Login_Log login_Log = new T_User_Login_Log()
{
Channel = this.AppRequestInfo.Channel,
@@ -151,6 +149,42 @@ namespace CloudGaming.Code.Account
return accountLogIn;
}
#region 注册用户
+
+ ///
+ /// 设备Redis缓存管理方法
+ ///
+ ///
+ ///
+ ///
+ ///
+ private async Task ManageDeviceCacheAsync(int userId, string currentTokenMd5, List deviceList)
+ {
+
+
+
+ // 获取用户当前所有的登录缓存key
+ var existingKeys = await RedisServerCache.ScanKeysAsync($"user:login:{userId}:*");
+
+ if (existingKeys != null && existingKeys.Count > 0)
+ {
+ // 查找和移除不在当前设备列表中的旧设备
+ var activeKeys = deviceList.Select(dev => $"user:login:{userId}:{dev.TokenMd5}").ToHashSet();
+
+ foreach (var key in existingKeys)
+ {
+ if (!activeKeys.Contains(key))
+ {
+ // 将无效设备状态标记为过期
+ await RedisCache.StringSetAsync(key, "0", TimeSpan.FromMinutes(10));
+ }
+ }
+ }
+ // 构建当前设备的Redis key
+ var currentDeviceKey = $"user:login:{userId}:{currentTokenMd5}";
+ // 创建当前设备的缓存记录
+ await RedisCache.StringSetAsync(currentDeviceKey, "1", TimeSpan.FromHours(1));
+ }
+
///
/// 注册新用户或更新现有用户信息
///
@@ -220,7 +254,7 @@ namespace CloudGaming.Code.Account
}
// 管理用户设备
- private async Task> ManageUserDevicesAsync(T_User user, IUserAccount account, string accessToken)
+ private async Task<(List, T_User_Token)> ManageUserDevicesAsync(T_User user, IUserAccount account, string accessToken)
{
var currentTime = DateTime.Now;
var dev = string.IsNullOrEmpty(account.DeviceNumber)
@@ -274,7 +308,7 @@ namespace CloudGaming.Code.Account
}
await Dao.DaoUser.Context.SaveChangesAsync();
- return userLoginList;
+ return (userLoginList, existingDevice);
}
// 更新设备令牌信息
diff --git a/src/CloudGaming/Code/CloudGaming.Code/Account/Login/PhoneUserLogin.cs b/src/CloudGaming/Code/CloudGaming.Code/Account/Login/PhoneUserLogin.cs
index ed596f8..98455d3 100644
--- a/src/CloudGaming/Code/CloudGaming.Code/Account/Login/PhoneUserLogin.cs
+++ b/src/CloudGaming/Code/CloudGaming.Code/Account/Login/PhoneUserLogin.cs
@@ -34,7 +34,7 @@ namespace CloudGaming.Code.Account.Login
throw MessageBox.Show(ResonseCode.ParamError, "验证码不能为空");
}
//判断是否是测试账号
- if (!loginParams.PhoneNumber.Contains("999999999") && loginParams.VerificationCode == "1112")
+ if (!loginParams.PhoneNumber.Contains("99999999") && loginParams.VerificationCode == "1112")
{
if (!PhoneNumberValidator.IsPhoneNumber(loginParams.PhoneNumber))
{
diff --git a/src/CloudGaming/Code/CloudGaming.Code/AppExtend/AppConfigurationExtend.cs b/src/CloudGaming/Code/CloudGaming.Code/AppExtend/AppConfigurationExtend.cs
index 92115ad..b11f00c 100644
--- a/src/CloudGaming/Code/CloudGaming.Code/AppExtend/AppConfigurationExtend.cs
+++ b/src/CloudGaming/Code/CloudGaming.Code/AppExtend/AppConfigurationExtend.cs
@@ -1,5 +1,6 @@
using AgileConfig.Client;
+using CloudGaming.Code.DataAccess;
using CloudGaming.Code.DataAccess.MultiTenantUtil;
using HuanMeng.DotNetCore.CacheHelper;
@@ -148,6 +149,16 @@ namespace CloudGaming.Code.AppExtend
{
return RedisConnection.GetRedis(appConfig.RedisConnectionString);
}
+ ///
+ /// 获取Dao
+ ///
+ ///
+ ///
+ public static DAO GetDAO(this IServiceProvider serviceProvider)
+ {
+ return new DAO(serviceProvider);
+ }
+
///
///
diff --git a/src/CloudGaming/Code/CloudGaming.Code/AppExtend/CloudGamingBase.cs b/src/CloudGaming/Code/CloudGaming.Code/AppExtend/CloudGamingBase.cs
index 7c2b7f1..ec1a2c5 100644
--- a/src/CloudGaming/Code/CloudGaming.Code/AppExtend/CloudGamingBase.cs
+++ b/src/CloudGaming/Code/CloudGaming.Code/AppExtend/CloudGamingBase.cs
@@ -169,6 +169,21 @@ namespace CloudGaming.Code.AppExtend
return _redis;
}
}
+ private IServer _redisServer;
+ ///
+ /// 数据库查询
+ ///
+ public IServer RedisServerCache
+ {
+ get
+ {
+ if (_redisServer == null)
+ {
+ _redisServer = RedisConnection.GetServer(AppConfig.RedisConnectionString);
+ }
+ return _redisServer;
+ }
+ }
#endregion
#region 缓存
@@ -223,7 +238,7 @@ namespace CloudGaming.Code.AppExtend
isChecking = new AppConfigBLL(this._serviceProvider).GetAppIsChecking();
RedisCache.StringSet(key, isChecking, new TimeSpan(1, 0, 0));
}
-
+
}
return isChecking ?? false;
}
diff --git a/src/CloudGaming/Code/CloudGaming.Code/AppExtend/JwtTokenManageExtension.cs b/src/CloudGaming/Code/CloudGaming.Code/AppExtend/JwtTokenManageExtension.cs
index b5f26aa..11c9b2a 100644
--- a/src/CloudGaming/Code/CloudGaming.Code/AppExtend/JwtTokenManageExtension.cs
+++ b/src/CloudGaming/Code/CloudGaming.Code/AppExtend/JwtTokenManageExtension.cs
@@ -43,6 +43,7 @@ namespace CloudGaming.Code.AppExtend
options.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
}).AddJwtBearer(options =>
{
+
options.RequireHttpsMetadata = true;
options.SaveToken = true;
//调试使用
@@ -70,20 +71,62 @@ namespace CloudGaming.Code.AppExtend
{
OnTokenValidated = async context =>
{
- var userId = context.Principal.FindFirst("userId")?.Value;
- if (userId == null || true)
+
+ var token = context.Request.Headers.GetAuthorization();
+ if (string.IsNullOrEmpty(token))
{
- context.Fail("Token missing userId claim.");
+ context.Fail("非法请求接口");
+ return;
+ }
+ var _userId = context.Principal.FindFirst("userId")?.Value;
+ int userId = 0;
+ if (_userId == null && !int.TryParse(_userId, out userId))
+ {
+ context.Fail("请求标头错误");
+ return;
+ }
+ var tokenMd5 = MD5Encryption.ComputeMD5Hash(token);
+ var appConfig = context.HttpContext.RequestServices.GetRequiredService();
+ var host = context.Request.Host.Host;
+ var app = AppConfigurationExtend.GetAppConfig(host);
+ if (app == null)
+ {
+ context.Fail("未配置租户");
+ return;
+ }
+ var redis = app.GetRedisDataBase();
+ var isUserExpire = await redis.StringGetAsync($"user:login:{_userId}:{tokenMd5}");
+
+ var isUserExpireStatus = isUserExpire.ToString();
+ if (string.IsNullOrEmpty(isUserExpireStatus))
+ {
+ //再次去数据库中验证
+ //IServiceProvider
+ var _serviceProvider = context.HttpContext.RequestServices.GetRequiredService();
+ var dao = _serviceProvider.GetDAO();
+ var c = await dao.DaoUser.Context.T_User_Token.Where(it => it.UserId == userId && it.TokenMd5 == tokenMd5).CountAsync();
+ if (c <= 0)
+ {
+ //添加过期信息
+ await redis.StringSetAsync($"user:login:{_userId}:{tokenMd5}", "0", TimeSpan.FromMinutes(15));
+ //app.get
+ context.Fail("用户状态错误");
+ return;
+ }
+ else
+ {
+ isUserExpireStatus = "1";
+ //添加过期信息
+ await redis.StringSetAsync($"user:login:{_userId}:{tokenMd5}", "1", TimeSpan.FromMinutes(30));
+ }
+ }
+ if (isUserExpireStatus == "0")
+ {
+ //设备被顶掉
+ context.Fail("用户在其它设备登录");
return;
}
- //// 你可以调用数据库或其他服务来验证用户状态
- //var userService = context.HttpContext.RequestServices.GetRequiredService();
- //var user = await userService.GetUserByIdAsync(userId);
- //if (user == null || !user.IsActive)
- //{
- // context.Fail("User is inactive or not found.");
- //}
},
// 处理认证失败的事件
OnAuthenticationFailed = context =>
diff --git a/src/CloudGaming/Console/CloudGaming.CreateDataBase/Program.cs b/src/CloudGaming/Console/CloudGaming.CreateDataBase/Program.cs
index 3967f79..d5cef5b 100644
--- a/src/CloudGaming/Console/CloudGaming.CreateDataBase/Program.cs
+++ b/src/CloudGaming/Console/CloudGaming.CreateDataBase/Program.cs
@@ -7,17 +7,20 @@ using Microsoft.EntityFrameworkCore;
using Newtonsoft.Json;
//var jsopn = JsonConvert.SerializeObject(new PhoneLoginParams());
//Server=192.168.1.17;Database=CloudGamingUser;User Id=sa;Password=Dbt@com@123;TrustServerCertificate=true;
-var optionsBuilder = new DbContextOptionsBuilder();
-var option = optionsBuilder.UseSqlServer("Server=192.168.195.6;Database=CloudGamingPhone;User Id=sa;Password=Dbt@com@123;TrustServerCertificate=true;").Options;
-CloudGamingPhoneContext cloudGamingPhoneContext = new CloudGamingPhoneContext(option);
-//cloudGamingPhoneContext.Database.EnsureCreated();
-var x = cloudGamingPhoneContext.T_Epg_Cfg.Count();
-var ccc = cloudGamingPhoneContext.T_Epg_Cfg.ToList();
-Console.WriteLine("查询" + x.ToString());
-Console.ReadKey();
+//var optionsBuilder = new DbContextOptionsBuilder();
+//var option = optionsBuilder.UseSqlServer("Server=192.168.195.6;Database=CloudGamingPhone;User Id=sa;Password=Dbt@com@123;TrustServerCertificate=true;").Options;
+//CloudGamingPhoneContext cloudGamingPhoneContext = new CloudGamingPhoneContext(option);
+////cloudGamingPhoneContext.Database.EnsureCreated();
+//var x = cloudGamingPhoneContext.T_GameCBT.Count();
+//var ccc = cloudGamingPhoneContext.T_Epg_Cfg.ToList();
+//Console.WriteLine("查询" + x.ToString());
+//Console.ReadKey();
//Server=192.168.1.17;Database=CloudGamingPhone;User Id=sa;Password=Dbt@com@123;TrustServerCertificate=true;
-//var optionsBuilder1 = new DbContextOptionsBuilder();
-//var option1 = optionsBuilder1.UseSqlServer("Server=192.168.1.17;Database=CloudGamingUser;User Id=sa;Password=Dbt@com@123;TrustServerCertificate=true;").Options;
-//CloudGamingUserContext cloud = new CloudGamingUserContext(option1);
+var optionsBuilder1 = new DbContextOptionsBuilder();
+var option1 = optionsBuilder1.UseSqlServer("Server=192.168.195.6;Database=CloudGamingUser;User Id=sa;Password=Dbt@com@123;TrustServerCertificate=true;").Options;
+CloudGamingUserContext cloud = new CloudGamingUserContext(option1);
+var xxx = cloud.T_User.Count();
+Console.WriteLine("查询" + xxx.ToString());
+Console.ReadKey();
//cloud.Database.EnsureCreated();
//cloud.Database.
\ No newline at end of file
diff --git a/src/CloudGaming/Model/CloudGaming.GameModel/Db/Db_Ext/CloudGamingCBTContext.cs b/src/CloudGaming/Model/CloudGaming.GameModel/Db/Db_Ext/CloudGamingCBTContext.cs
index da20118..2c953e1 100644
--- a/src/CloudGaming/Model/CloudGaming.GameModel/Db/Db_Ext/CloudGamingCBTContext.cs
+++ b/src/CloudGaming/Model/CloudGaming.GameModel/Db/Db_Ext/CloudGamingCBTContext.cs
@@ -65,8 +65,8 @@ public partial class CloudGamingCBTContext : DbContext
public virtual DbSet T_User_Login_Log { get; set; }
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
- => optionsBuilder.UseSqlServer("Server=192.168.1.17;Database=CloudGamingCBT;User Id=sa;Password=Dbt@com@123;TrustServerCertificate=true;");
-
+ {// => optionsBuilder.UseSqlServer("Server=192.168.195.6;Database=CloudGamingCBT;User Id=sa;Password=Dbt@com@123;TrustServerCertificate=true;");
+ }
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity(entity =>
diff --git a/src/CloudGaming/Model/CloudGaming.GameModel/Db/Db_Game/CloudGamingGameContext.cs b/src/CloudGaming/Model/CloudGaming.GameModel/Db/Db_Game/CloudGamingGameContext.cs
index 25cbf26..d7da168 100644
--- a/src/CloudGaming/Model/CloudGaming.GameModel/Db/Db_Game/CloudGamingGameContext.cs
+++ b/src/CloudGaming/Model/CloudGaming.GameModel/Db/Db_Game/CloudGamingGameContext.cs
@@ -1,4 +1,4 @@
-using System;
+using System;
using System.Collections.Generic;
using Microsoft.EntityFrameworkCore;
@@ -71,6 +71,7 @@ public partial class CloudGamingGameContext : DbContext
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{// => optionsBuilder.UseSqlServer("Server=192.168.195.6;Database=CloudGamingGame;User Id=sa;Password=Dbt@com@123;TrustServerCertificate=true;");
+ //optionsBuilder.uses
}
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
diff --git a/src/CloudGaming/Model/CloudGaming.GameModel/Db/efcore-gen.md b/src/CloudGaming/Model/CloudGaming.GameModel/Db/efcore-gen.md
index fb9e77d..b9bd754 100644
--- a/src/CloudGaming/Model/CloudGaming.GameModel/Db/efcore-gen.md
+++ b/src/CloudGaming/Model/CloudGaming.GameModel/Db/efcore-gen.md
@@ -9,5 +9,5 @@ dotnet ef dbcontext scaffold "Server=192.168.195.6;Database=CloudGamingGame;User
--Ext
dotnet ef dbcontext scaffold "Server=192.168.1.17;Database=CloudGamingCBT;User Id=sa;Password=Dbt@com@123;TrustServerCertificate=true;" Microsoft.EntityFrameworkCore.SqlServer -o Db/Db_Ext/ --use-database-names --no-pluralize --force
内网穿透
-dotnet ef dbcontext scaffold "Server=192.168.195.6;Database=CloudGamingGame;User Id=sa;Password=Dbt@com@123;TrustServerCertificate=true;" Microsoft.EntityFrameworkCore.SqlServer -o Db/Db_Game/ --use-database-names --no-pluralize --force
+dotnet ef dbcontext scaffold "Server=192.168.195.6;Database=CloudGamingCBT;User Id=sa;Password=Dbt@com@123;TrustServerCertificate=true;" Microsoft.EntityFrameworkCore.SqlServer -o Db/Db_Ext/ --use-database-names --no-pluralize --force
```
\ No newline at end of file
diff --git a/src/CloudGaming/Model/CloudGaming.Model/DbSqlServer/Db_Phone/CloudGamingPhoneContext.cs b/src/CloudGaming/Model/CloudGaming.Model/DbSqlServer/Db_Phone/CloudGamingPhoneContext.cs
index eda4315..a9049b6 100644
--- a/src/CloudGaming/Model/CloudGaming.Model/DbSqlServer/Db_Phone/CloudGamingPhoneContext.cs
+++ b/src/CloudGaming/Model/CloudGaming.Model/DbSqlServer/Db_Phone/CloudGamingPhoneContext.cs
@@ -147,6 +147,7 @@ public partial class CloudGamingPhoneContext : MultiTenantDbContext//DbContext
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
// => optionsBuilder.UseSqlServer("Server=192.168.195.6;Database=CloudGamingPhone;User Id=sa;Password=Dbt@com@123;TrustServerCertificate=true;");
{
+ optionsBuilder.UseSqlServer("Server=192.168.195.6;Database=CloudGamingPhone;User Id=sa;Password=Dbt@com@123;TrustServerCertificate=true;");
}
protected override void OnModelCreating(ModelBuilder modelBuilder)
diff --git a/src/CloudGaming/Utile/HuanMeng.DotNetCore/Redis/RedisConnection.cs b/src/CloudGaming/Utile/HuanMeng.DotNetCore/Redis/RedisConnection.cs
index cee90e6..54f59a9 100644
--- a/src/CloudGaming/Utile/HuanMeng.DotNetCore/Redis/RedisConnection.cs
+++ b/src/CloudGaming/Utile/HuanMeng.DotNetCore/Redis/RedisConnection.cs
@@ -1,3 +1,4 @@
+using Microsoft.EntityFrameworkCore.Metadata.Internal;
using Microsoft.Identity.Client;
using Newtonsoft.Json;
@@ -25,6 +26,9 @@ namespace HuanMeng.DotNetCore.Redis
///
public static ConcurrentDictionary Redis { get; set; } = new ConcurrentDictionary();
+ ///
+ /// 数据库查询
+ ///
public static ConcurrentDictionary RedisServer { get; set; } = new ConcurrentDictionary();
@@ -55,13 +59,62 @@ namespace HuanMeng.DotNetCore.Redis
if (!RedisServer.TryGetValue(redisConnection, out var server))
{
var redis = ConnectionMultiplexer.Connect(redisConnection);
- server = redis.GetServer("", "");
+ var serverConn = ParseIpPortAndDatabase(redisConnection);
+ server = redis.GetServer(serverConn.ip, serverConn.port);
//server.key
//redis.GetServer()
RedisServer.TryAdd(redisConnection, server);
}
return server;
}
+ private static (string ip, int port, int database) ParseIpPortAndDatabase(string connectionString)
+ {
+ // 默认端口号和默认数据库
+ int defaultPort = 6379;
+ int defaultDatabase = 0;
+
+ if (string.IsNullOrEmpty(connectionString))
+ {
+ return ("localhost", defaultPort, defaultDatabase);
+ }
+
+ // 按逗号分割,获取主机部分和其他参数
+ var parts = connectionString.Split(',');
+ var hostPart = parts[0]; // 例如:"192.168.195.6:6379"
+
+ // 检查是否包含端口号
+ string ip;
+ int port;
+
+ if (hostPart.Contains(":"))
+ {
+ var hostParts = hostPart.Split(':');
+ ip = hostParts[0];
+ port = int.TryParse(hostParts[1], out int parsedPort) ? parsedPort : defaultPort;
+ }
+ else
+ {
+ // 如果没有端口号,使用默认端口
+ ip = hostPart;
+ port = defaultPort;
+ }
+
+ // 解析 defaultDatabase 参数
+ int database = defaultDatabase;
+ foreach (var part in parts)
+ {
+ if (part.StartsWith("defaultDatabase=", StringComparison.OrdinalIgnoreCase))
+ {
+ var dbPart = part.Split('=');
+ if (dbPart.Length == 2 && int.TryParse(dbPart[1], out int parsedDb))
+ {
+ database = parsedDb;
+ }
+ }
+ }
+
+ return (ip, port, database);
+ }
///
///
@@ -141,7 +194,26 @@ namespace HuanMeng.DotNetCore.Redis
///
public static List ScanKeys(this IServer server, string pattern, int pageSize = 100)
{
- var matchingKeys = server.Keys(pattern: pattern).Select(it => it.ToString()).ToList();
+ var matchingKeys = server.Keys(pattern: pattern, pageSize: pageSize).Select(it => it.ToString()).ToList();
+ return matchingKeys;
+ }
+ ///
+ /// 异步模糊查询key
+ ///
+ ///
+ ///
+ ///
+ ///
+ /// 匹配的键的字符串列表
+ public static async Task> ScanKeysAsync(this IServer server, string pattern, int pageSize = 100, int database = -1)
+ {
+ var matchingKeys = new List();
+
+ await foreach (var key in server.KeysAsync(database: database, pattern: pattern, pageSize: pageSize))
+ {
+ matchingKeys.Add(key.ToString());
+ }
+
return matchingKeys;
}
}
diff --git a/src/CloudGaming/Utile/HuanMeng.DotNetCore/Utility/HttpContextExtensions.cs b/src/CloudGaming/Utile/HuanMeng.DotNetCore/Utility/HttpContextExtensions.cs
index 65d3311..f67c3c6 100644
--- a/src/CloudGaming/Utile/HuanMeng.DotNetCore/Utility/HttpContextExtensions.cs
+++ b/src/CloudGaming/Utile/HuanMeng.DotNetCore/Utility/HttpContextExtensions.cs
@@ -35,5 +35,30 @@ namespace HuanMeng.DotNetCore.Utility
// 如果X-Forwarded-For头部不存在,使用RemoteIpAddress
return context.Connection.RemoteIpAddress?.ToString();
}
+
+ ///
+ /// 从请求头中提取Authorization信息(JWT)。
+ ///
+ /// 请求头字典。
+ /// 如果包含有效的Authorization头,则返回JWT Token,否则返回null。
+ public static string? GetAuthorization(this IHeaderDictionary headers)
+ {
+ // 尝试从请求头中获取Authorization字段
+ if (headers.TryGetValue("Authorization", out var authHeaderObj))
+ {
+ // 获取Authorization头的值并移除"Bearer "前缀
+ var authHeader = authHeaderObj.ToString();
+
+ // 如果Authorization以"Bearer "开头,提取JWT Token
+ if (!string.IsNullOrEmpty(authHeader) && authHeader.StartsWith("Bearer ", StringComparison.OrdinalIgnoreCase))
+ {
+ return authHeader.Substring(7).Trim(); // 直接返回JWT Token
+ }
+ }
+
+ // 如果没有Authorization头或格式不正确,返回null
+ return null;
+ }
+
}
}