合并代码

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

View File

@ -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
/// <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>
@ -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 dev = string.IsNullOrEmpty(account.DeviceNumber)
@ -274,7 +308,7 @@ namespace CloudGaming.Code.Account
}
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, "验证码不能为空");
}
//判断是否是测试账号
if (!loginParams.PhoneNumber.Contains("999999999") && loginParams.VerificationCode == "1112")
if (!loginParams.PhoneNumber.Contains("99999999") && loginParams.VerificationCode == "1112")
{
if (!PhoneNumberValidator.IsPhoneNumber(loginParams.PhoneNumber))
{

View File

@ -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);
}
/// <summary>
/// 获取Dao
/// </summary>
/// <param name="appConfig"></param>
/// <returns></returns>
public static DAO GetDAO(this IServiceProvider serviceProvider)
{
return new DAO(serviceProvider);
}
/// <summary>
///

View File

@ -169,6 +169,21 @@ namespace CloudGaming.Code.AppExtend
return _redis;
}
}
private IServer _redisServer;
/// <summary>
/// 数据库查询
/// </summary>
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;
}

View File

@ -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<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;
}
//// 你可以调用数据库或其他服务来验证用户状态
//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 =>

View File

@ -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<CloudGamingPhoneContext>();
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<CloudGamingPhoneContext>();
//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<CloudGamingUserContext>();
//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<CloudGamingUserContext>();
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.

View File

@ -65,8 +65,8 @@ public partial class CloudGamingCBTContext : DbContext
public virtual DbSet<T_User_Login_Log> 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<T_App_Config>(entity =>

View File

@ -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)
{

View File

@ -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
```

View File

@ -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)

View File

@ -1,3 +1,4 @@
using Microsoft.EntityFrameworkCore.Metadata.Internal;
using Microsoft.Identity.Client;
using Newtonsoft.Json;
@ -25,6 +26,9 @@ namespace HuanMeng.DotNetCore.Redis
/// </summary>
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>();
@ -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);
}
/// <summary>
///
@ -141,7 +194,26 @@ namespace HuanMeng.DotNetCore.Redis
/// <returns></returns>
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;
}
}

View File

@ -35,5 +35,30 @@ namespace HuanMeng.DotNetCore.Utility
// 如果X-Forwarded-For头部不存在使用RemoteIpAddress
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;
}
}
}