合并代码

This commit is contained in:
zpc 2024-11-13 22:55:54 +08:00
commit c89d729bf2
13 changed files with 244 additions and 37 deletions

View File

@ -151,6 +151,7 @@ var app = builder.Build();
// app.UseSwagger(); // app.UseSwagger();
// app.UseSwaggerUI(); // app.UseSwaggerUI();
//} //}
app.UseSwagger(); app.UseSwagger();
app.UseSwaggerUI(c => app.UseSwaggerUI(c =>
{ {
@ -165,6 +166,7 @@ app.UseSwaggerUI(c =>
app.UseHttpsRedirection(); app.UseHttpsRedirection();
//注册身份验证中间件
app.UseAuthorization(); app.UseAuthorization();
//数据库中间件 //数据库中间件
app.UseMultiTenant(); app.UseMultiTenant();

View File

@ -126,13 +126,11 @@ namespace CloudGaming.Code.Account
Token = jwt.AccessToken, Token = jwt.AccessToken,
UserId = user.Id, UserId = user.Id,
}; };
//创建设备号 //获取登录的设备
var dev = await ManageUserDevicesAsync(user, account, jwt.AccessToken); var (deviceList, currentDevice) = await ManageUserDevicesAsync(user, account, jwt.AccessToken);
var key = $"user:login:{user.Id}"; // 管理设备和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() T_User_Login_Log login_Log = new T_User_Login_Log()
{ {
Channel = this.AppRequestInfo.Channel, Channel = this.AppRequestInfo.Channel,
@ -151,6 +149,42 @@ namespace CloudGaming.Code.Account
return accountLogIn; return accountLogIn;
} }
#region #region
/// <summary>
/// 设备Redis缓存管理方法
/// </summary>
/// <param name="userId"></param>
/// <param name="currentTokenMd5"></param>
/// <param name="deviceList"></param>
/// <returns></returns>
private async Task ManageDeviceCacheAsync(int userId, string currentTokenMd5, List<T_User_Token> 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));
}
/// <summary> /// <summary>
/// 注册新用户或更新现有用户信息 /// 注册新用户或更新现有用户信息
/// </summary> /// </summary>
@ -220,7 +254,7 @@ namespace CloudGaming.Code.Account
} }
// 管理用户设备 // 管理用户设备
private async Task<List<T_User_Token>> ManageUserDevicesAsync(T_User user, IUserAccount account, string accessToken) private async Task<(List<T_User_Token>, T_User_Token)> ManageUserDevicesAsync(T_User user, IUserAccount account, string accessToken)
{ {
var currentTime = DateTime.Now; var currentTime = DateTime.Now;
var dev = string.IsNullOrEmpty(account.DeviceNumber) var dev = string.IsNullOrEmpty(account.DeviceNumber)
@ -274,7 +308,7 @@ namespace CloudGaming.Code.Account
} }
await Dao.DaoUser.Context.SaveChangesAsync(); await Dao.DaoUser.Context.SaveChangesAsync();
return userLoginList; return (userLoginList, existingDevice);
} }
// 更新设备令牌信息 // 更新设备令牌信息

View File

@ -34,7 +34,7 @@ namespace CloudGaming.Code.Account.Login
throw MessageBox.Show(ResonseCode.ParamError, "验证码不能为空"); 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)) if (!PhoneNumberValidator.IsPhoneNumber(loginParams.PhoneNumber))
{ {

View File

@ -1,5 +1,6 @@
using AgileConfig.Client; using AgileConfig.Client;
using CloudGaming.Code.DataAccess;
using CloudGaming.Code.DataAccess.MultiTenantUtil; using CloudGaming.Code.DataAccess.MultiTenantUtil;
using HuanMeng.DotNetCore.CacheHelper; using HuanMeng.DotNetCore.CacheHelper;
@ -148,6 +149,16 @@ namespace CloudGaming.Code.AppExtend
{ {
return RedisConnection.GetRedis(appConfig.RedisConnectionString); return RedisConnection.GetRedis(appConfig.RedisConnectionString);
} }
/// <summary>
/// 获取Dao
/// </summary>
/// <param name="appConfig"></param>
/// <returns></returns>
public static DAO GetDAO(this IServiceProvider serviceProvider)
{
return new DAO(serviceProvider);
}
/// <summary> /// <summary>
/// ///

View File

@ -169,6 +169,21 @@ namespace CloudGaming.Code.AppExtend
return _redis; return _redis;
} }
} }
private IServer _redisServer;
/// <summary>
/// 数据库查询
/// </summary>
public IServer RedisServerCache
{
get
{
if (_redisServer == null)
{
_redisServer = RedisConnection.GetServer(AppConfig.RedisConnectionString);
}
return _redisServer;
}
}
#endregion #endregion
#region #region

View File

@ -43,6 +43,7 @@ namespace CloudGaming.Code.AppExtend
options.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme; options.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
}).AddJwtBearer(options => }).AddJwtBearer(options =>
{ {
options.RequireHttpsMetadata = true; options.RequireHttpsMetadata = true;
options.SaveToken = true; options.SaveToken = true;
//调试使用 //调试使用
@ -70,20 +71,62 @@ namespace CloudGaming.Code.AppExtend
{ {
OnTokenValidated = async context => 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<AppConfig>();
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<IServiceProvider>();
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; return;
} }
//// 你可以调用数据库或其他服务来验证用户状态
//var userService = context.HttpContext.RequestServices.GetRequiredService<IUserService>();
//var user = await userService.GetUserByIdAsync(userId);
//if (user == null || !user.IsActive)
//{
// context.Fail("User is inactive or not found.");
//}
}, },
// 处理认证失败的事件 // 处理认证失败的事件
OnAuthenticationFailed = context => OnAuthenticationFailed = context =>

View File

@ -7,17 +7,20 @@ using Microsoft.EntityFrameworkCore;
using Newtonsoft.Json; using Newtonsoft.Json;
//var jsopn = JsonConvert.SerializeObject(new PhoneLoginParams()); //var jsopn = JsonConvert.SerializeObject(new PhoneLoginParams());
//Server=192.168.1.17;Database=CloudGamingUser;User Id=sa;Password=Dbt@com@123;TrustServerCertificate=true; //Server=192.168.1.17;Database=CloudGamingUser;User Id=sa;Password=Dbt@com@123;TrustServerCertificate=true;
var optionsBuilder = new DbContextOptionsBuilder<CloudGamingPhoneContext>(); //var optionsBuilder = new DbContextOptionsBuilder<CloudGamingPhoneContext>();
var option = optionsBuilder.UseSqlServer("Server=192.168.195.6;Database=CloudGamingPhone;User Id=sa;Password=Dbt@com@123;TrustServerCertificate=true;").Options; //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 cloudGamingPhoneContext = new CloudGamingPhoneContext(option);
//cloudGamingPhoneContext.Database.EnsureCreated(); ////cloudGamingPhoneContext.Database.EnsureCreated();
var x = cloudGamingPhoneContext.T_Epg_Cfg.Count(); //var x = cloudGamingPhoneContext.T_GameCBT.Count();
var ccc = cloudGamingPhoneContext.T_Epg_Cfg.ToList(); //var ccc = cloudGamingPhoneContext.T_Epg_Cfg.ToList();
Console.WriteLine("查询" + x.ToString()); //Console.WriteLine("查询" + x.ToString());
Console.ReadKey(); //Console.ReadKey();
//Server=192.168.1.17;Database=CloudGamingPhone;User Id=sa;Password=Dbt@com@123;TrustServerCertificate=true; //Server=192.168.1.17;Database=CloudGamingPhone;User Id=sa;Password=Dbt@com@123;TrustServerCertificate=true;
//var optionsBuilder1 = new DbContextOptionsBuilder<CloudGamingUserContext>(); var optionsBuilder1 = new DbContextOptionsBuilder<CloudGamingUserContext>();
//var option1 = optionsBuilder1.UseSqlServer("Server=192.168.1.17;Database=CloudGamingUser;User Id=sa;Password=Dbt@com@123;TrustServerCertificate=true;").Options; 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); CloudGamingUserContext cloud = new CloudGamingUserContext(option1);
var xxx = cloud.T_User.Count();
Console.WriteLine("查询" + xxx.ToString());
Console.ReadKey();
//cloud.Database.EnsureCreated(); //cloud.Database.EnsureCreated();
//cloud.Database. //cloud.Database.

View File

@ -65,8 +65,8 @@ public partial class CloudGamingCBTContext : DbContext
public virtual DbSet<T_User_Login_Log> T_User_Login_Log { get; set; } public virtual DbSet<T_User_Login_Log> T_User_Login_Log { get; set; }
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder) 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) protected override void OnModelCreating(ModelBuilder modelBuilder)
{ {
modelBuilder.Entity<T_App_Config>(entity => modelBuilder.Entity<T_App_Config>(entity =>

View File

@ -1,4 +1,4 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore;
@ -71,6 +71,7 @@ public partial class CloudGamingGameContext : DbContext
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder) 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.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) protected override void OnModelCreating(ModelBuilder modelBuilder)
{ {

View File

@ -9,5 +9,5 @@ dotnet ef dbcontext scaffold "Server=192.168.195.6;Database=CloudGamingGame;User
--Ext --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.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
``` ```

View File

@ -147,6 +147,7 @@ public partial class CloudGamingPhoneContext : MultiTenantDbContext//DbContext
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder) 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;");
{ {
optionsBuilder.UseSqlServer("Server=192.168.195.6;Database=CloudGamingPhone;User Id=sa;Password=Dbt@com@123;TrustServerCertificate=true;");
} }
protected override void OnModelCreating(ModelBuilder modelBuilder) protected override void OnModelCreating(ModelBuilder modelBuilder)

View File

@ -1,3 +1,4 @@
using Microsoft.EntityFrameworkCore.Metadata.Internal;
using Microsoft.Identity.Client; using Microsoft.Identity.Client;
using Newtonsoft.Json; using Newtonsoft.Json;
@ -25,6 +26,9 @@ namespace HuanMeng.DotNetCore.Redis
/// </summary> /// </summary>
public static ConcurrentDictionary<string, IDatabase> Redis { get; set; } = new ConcurrentDictionary<string, IDatabase>(); public static ConcurrentDictionary<string, IDatabase> Redis { get; set; } = new ConcurrentDictionary<string, IDatabase>();
/// <summary>
/// 数据库查询
/// </summary>
public static ConcurrentDictionary<string, IServer> RedisServer { get; set; } = new ConcurrentDictionary<string, IServer>(); public static ConcurrentDictionary<string, IServer> RedisServer { get; set; } = new ConcurrentDictionary<string, IServer>();
@ -55,13 +59,62 @@ namespace HuanMeng.DotNetCore.Redis
if (!RedisServer.TryGetValue(redisConnection, out var server)) if (!RedisServer.TryGetValue(redisConnection, out var server))
{ {
var redis = ConnectionMultiplexer.Connect(redisConnection); var redis = ConnectionMultiplexer.Connect(redisConnection);
server = redis.GetServer("", ""); var serverConn = ParseIpPortAndDatabase(redisConnection);
server = redis.GetServer(serverConn.ip, serverConn.port);
//server.key //server.key
//redis.GetServer() //redis.GetServer()
RedisServer.TryAdd(redisConnection, server); RedisServer.TryAdd(redisConnection, server);
} }
return 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);
}
/// <summary> /// <summary>
/// ///
@ -141,7 +194,26 @@ namespace HuanMeng.DotNetCore.Redis
/// <returns></returns> /// <returns></returns>
public static List<string> ScanKeys(this IServer server, string pattern, int pageSize = 100) public static List<string> 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;
}
/// <summary>
/// 异步模糊查询key
/// </summary>
/// <param name="server"></param>
/// <param name="pattern"></param>
/// <param name="pageSize"></param>
/// <param name="database"></param>
/// <returns>匹配的键的字符串列表</returns>
public static async Task<List<string>> ScanKeysAsync(this IServer server, string pattern, int pageSize = 100, int database = -1)
{
var matchingKeys = new List<string>();
await foreach (var key in server.KeysAsync(database: database, pattern: pattern, pageSize: pageSize))
{
matchingKeys.Add(key.ToString());
}
return matchingKeys; return matchingKeys;
} }
} }

View File

@ -35,5 +35,30 @@ namespace HuanMeng.DotNetCore.Utility
// 如果X-Forwarded-For头部不存在使用RemoteIpAddress // 如果X-Forwarded-For头部不存在使用RemoteIpAddress
return context.Connection.RemoteIpAddress?.ToString(); return context.Connection.RemoteIpAddress?.ToString();
} }
/// <summary>
/// 从请求头中提取Authorization信息JWT
/// </summary>
/// <param name="headers">请求头字典。</param>
/// <returns>如果包含有效的Authorization头则返回JWT Token否则返回null。</returns>
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;
}
} }
} }