diff --git a/FunGame.Server/Controllers/DataRequestController.cs b/FunGame.Server/Controllers/DataRequestController.cs index 1da2c98..996a4a3 100644 --- a/FunGame.Server/Controllers/DataRequestController.cs +++ b/FunGame.Server/Controllers/DataRequestController.cs @@ -4,6 +4,7 @@ using Milimoe.FunGame.Core.Api.Utility; using Milimoe.FunGame.Core.Entity; using Milimoe.FunGame.Core.Interface.Base; using Milimoe.FunGame.Core.Library.Common.Addon; +using Milimoe.FunGame.Core.Library.Common.Event; using Milimoe.FunGame.Core.Library.Constant; using Milimoe.FunGame.Core.Library.SQLScript.Common; using Milimoe.FunGame.Core.Library.SQLScript.Entity; @@ -27,9 +28,7 @@ namespace Milimoe.FunGame.Server.Controller private DataRequestType _lastRequest = DataRequestType.UnKnown; private readonly bool[] _isReadyCheckCD = [false, false]; - protected DataSet _dsUser = new(); protected string _username = ""; - protected Guid _checkLoginKey = Guid.Empty; protected bool _isMatching; /// @@ -150,12 +149,11 @@ namespace Milimoe.FunGame.Server.Controller { ServerHelper.WriteLine(Server.GetClientName() + " -> " + DataRequestSet.GetTypeString(_lastRequest), InvokeMessageType.DataRequest); key = DataRequest.GetDictionaryJsonObject(requestData, "key"); - if (IsLoginKey(key)) + if (Server.IsLoginKey(key)) { // 从玩家列表移除 Server.RemoveUser(); Server.GetUsersCount(); - _checkLoginKey = Guid.Empty; msg = "你已成功退出登录! "; } } @@ -192,7 +190,7 @@ namespace Milimoe.FunGame.Server.Controller string gamemap = DataRequest.GetDictionaryJsonObject(requestData, "gamemap") ?? ""; bool isrank = DataRequest.GetDictionaryJsonObject(requestData, "isrank"); ServerHelper.WriteLine(Server.GetClientName() + " -> " + DataRequestSet.GetTypeString(_lastRequest) + " : " + RoomSet.GetTypeString(type) + " (" + string.Join(", ", [gamemodule, gamemap]) + ")", InvokeMessageType.DataRequest); - if (gamemodule == "" || gamemap == "" || Config.GameModuleLoader is null || !Config.GameModuleLoader.ModuleServers.ContainsKey(gamemodule) || !Config.GameModuleLoader.Maps.ContainsKey(gamemap)) + if (gamemodule == "" || gamemap == "" || FunGameSystem.GameModuleLoader is null || !FunGameSystem.GameModuleLoader.ModuleServers.ContainsKey(gamemodule) || !FunGameSystem.GameModuleLoader.Maps.ContainsKey(gamemap)) { ServerHelper.WriteLine("缺少对应的模组或地图,无法创建房间。"); resultData.Add("room", room); @@ -209,7 +207,7 @@ namespace Milimoe.FunGame.Server.Controller { // 防止重复 roomid = Verification.CreateVerifyCode(VerifyCodeType.MixVerifyCode, 7).ToUpper(); - if (Config.RoomList.GetRoom(roomid).Roomid == "-1") + if (FunGameSystem.RoomList.GetRoom(roomid).Roomid == "-1") { break; } @@ -224,7 +222,7 @@ namespace Milimoe.FunGame.Server.Controller if (SQLHelper.Result == SQLResult.Success && SQLHelper.DataSet.Tables[0].Rows.Count > 0) { room = Factory.GetRoom(SQLHelper.DataSet.Tables[0].Rows[0], user); - Config.RoomList.AddRoom(room); + FunGameSystem.RoomList.AddRoom(room); } } } @@ -240,7 +238,7 @@ namespace Milimoe.FunGame.Server.Controller private void UpdateRoom(Dictionary resultData) { ServerHelper.WriteLine(Server.GetClientName() + " -> " + DataRequestSet.GetTypeString(_lastRequest), InvokeMessageType.DataRequest); - resultData.Add("rooms", Config.RoomList.ListRoom); // 传RoomList + resultData.Add("rooms", FunGameSystem.RoomList.ListRoom); // 传RoomList } /// @@ -257,7 +255,7 @@ namespace Milimoe.FunGame.Server.Controller string roomid = DataRequest.GetDictionaryJsonObject(requestData, "roomid") ?? "-1"; bool isMaster = DataRequest.GetDictionaryJsonObject(requestData, "isMaster"); - if (roomid != "-1" && Config.RoomList.IsExist(roomid)) + if (roomid != "-1" && FunGameSystem.RoomList.IsExist(roomid)) { result = await Server.QuitRoom(roomid, isMaster); } @@ -285,15 +283,15 @@ namespace Milimoe.FunGame.Server.Controller SQLHelper.ExecuteDataSet(RoomQuery.Select_IsExistRoom(SQLHelper, roomid)); if (SQLHelper.Success) { - Config.RoomList.IntoRoom(roomid, Server.User); - Server.InRoom = Config.RoomList[roomid]; + FunGameSystem.RoomList.IntoRoom(roomid, Server.User); + Server.InRoom = FunGameSystem.RoomList[roomid]; await Server.SendClients(Server.Listener.ClientList.Where(c => c != null && roomid == c.InRoom.Roomid && c.User.Id != 0), SocketMessageType.Chat, Server.User.Username, DateTimeUtility.GetNowShortTime() + " [ " + Server.User.Username + " ] 进入了房间。"); result = true; } else { - Config.RoomList.RemoveRoom(roomid); + FunGameSystem.RoomList.RemoveRoom(roomid); } } } @@ -344,15 +342,15 @@ namespace Milimoe.FunGame.Server.Controller roomid = DataRequest.GetDictionaryJsonObject(requestData, "roomid") ?? "-1"; User user = Server.User; - if (roomid != "-1" && user.Id != 0 && user.Id != Config.RoomList.GetRoomMaster(roomid).Id && !Config.RoomList.GetReadyUserList(roomid).Contains(user)) + if (roomid != "-1" && user.Id != 0 && user.Id != FunGameSystem.RoomList.GetRoomMaster(roomid).Id && !FunGameSystem.RoomList.GetReadyUserList(roomid).Contains(user)) { - Config.RoomList.SetReady(roomid, user); + FunGameSystem.RoomList.SetReady(roomid, user); result = true; } } resultData.Add("result", result); - resultData.Add("ready", Config.RoomList.GetReadyUserList(roomid)); - resultData.Add("notready", Config.RoomList.GetNotReadyUserList(roomid)); + resultData.Add("ready", FunGameSystem.RoomList.GetReadyUserList(roomid)); + resultData.Add("notready", FunGameSystem.RoomList.GetNotReadyUserList(roomid)); } /// @@ -370,15 +368,15 @@ namespace Milimoe.FunGame.Server.Controller roomid = DataRequest.GetDictionaryJsonObject(requestData, "roomid") ?? "-1"; User user = Server.User; - if (roomid != "-1" && user.Id != 0 && user.Id != Config.RoomList.GetRoomMaster(roomid).Id && Config.RoomList.GetReadyUserList(roomid).Contains(user)) + if (roomid != "-1" && user.Id != 0 && user.Id != FunGameSystem.RoomList.GetRoomMaster(roomid).Id && FunGameSystem.RoomList.GetReadyUserList(roomid).Contains(user)) { - Config.RoomList.CancelReady(roomid, user); + FunGameSystem.RoomList.CancelReady(roomid, user); result = true; } } resultData.Add("result", result); - resultData.Add("ready", Config.RoomList.GetReadyUserList(roomid)); - resultData.Add("notready", Config.RoomList.GetNotReadyUserList(roomid)); + resultData.Add("ready", FunGameSystem.RoomList.GetReadyUserList(roomid)); + resultData.Add("notready", FunGameSystem.RoomList.GetNotReadyUserList(roomid)); } /// @@ -416,7 +414,7 @@ namespace Milimoe.FunGame.Server.Controller { if (isMaster) { - string[] usernames = [.. Config.RoomList.GetNotReadyUserList(roomid).Select(user => user.Username)]; + string[] usernames = [.. FunGameSystem.RoomList.GetNotReadyUserList(roomid).Select(user => user.Username)]; if (usernames.Length > 0) { if (_isReadyCheckCD[0] == false) @@ -437,7 +435,7 @@ namespace Milimoe.FunGame.Server.Controller } else { - List users = Config.RoomList.GetUsers(roomid); + List users = FunGameSystem.RoomList.GetUsers(roomid); if (users.Count < 2) { Server.SendSystemMessage(ShowMessageType.None, "玩家数量不足,无法开始游戏。", "", 0, Server.User.Username); @@ -455,7 +453,7 @@ namespace Milimoe.FunGame.Server.Controller { // 提醒房主开始游戏 Server.SendSystemMessage(ShowMessageType.None, "已提醒房主立即开始游戏。", "", 0, Server.User.Username); - Server.SendSystemMessage(ShowMessageType.Tip, "房间中的玩家已请求你立即开始游戏。", "请求开始", 10, Config.RoomList[roomid].RoomMaster.Username); + Server.SendSystemMessage(ShowMessageType.Tip, "房间中的玩家已请求你立即开始游戏。", "请求开始", 10, FunGameSystem.RoomList[roomid].RoomMaster.Username); _isReadyCheckCD[1] = true; TaskUtility.RunTimer(() => { @@ -476,15 +474,15 @@ namespace Milimoe.FunGame.Server.Controller Room room = General.HallInstance; if (roomid != "-1") { - room = Config.RoomList[roomid]; + room = FunGameSystem.RoomList[roomid]; } if (room.Roomid == "-1") return; // 启动服务器 TaskUtility.NewTask(() => { - if (Config.GameModuleLoader != null && Config.GameModuleLoader.ModuleServers.ContainsKey(room.GameModule)) + if (FunGameSystem.GameModuleLoader != null && FunGameSystem.GameModuleLoader.ModuleServers.ContainsKey(room.GameModule)) { - Server.NowGamingServer = Config.GameModuleLoader.GetServerMode(room.GameModule); + Server.NowGamingServer = FunGameSystem.GameModuleLoader.GetServerMode(room.GameModule); Dictionary all = Server.Listener.UserList.Cast().ToDictionary(k => k.User.Username, v => v); // 给其他玩家赋值模组服务器 foreach (IServerModel model in all.Values.Where(s => s.User.Username != Server.User.Username)) @@ -499,7 +497,7 @@ namespace Milimoe.FunGame.Server.Controller { if (serverTask != null && serverTask.Socket != null) { - Config.RoomList.CancelReady(roomid, serverTask.User); + FunGameSystem.RoomList.CancelReady(roomid, serverTask.User); serverTask.Send(SocketMessageType.StartGame, room, users); } } @@ -529,7 +527,11 @@ namespace Milimoe.FunGame.Server.Controller string password = DataRequest.GetDictionaryJsonObject(requestData, "password") ?? ""; string email = DataRequest.GetDictionaryJsonObject(requestData, "email") ?? ""; string verifycode = DataRequest.GetDictionaryJsonObject(requestData, "verifycode") ?? ""; - (msg, returnType, success) = DataRequestService.Reg(username, password, email, verifycode, Server.Socket?.ClientIP ?? "", SQLHelper, MailSender); + (msg, returnType, success) = DataRequestService.Reg(Server, username, password, email, verifycode, Server.Socket?.ClientIP ?? ""); + } + else + { + ServerHelper.WriteLine("客户端提供的参数不足。", InvokeMessageType.DataRequest, LogLevel.Warning); } resultData.Add("msg", msg); resultData.Add("type", returnType); @@ -547,106 +549,69 @@ namespace Milimoe.FunGame.Server.Controller /// private async Task Login(Dictionary requestData, Dictionary resultData) { + ServerHelper.WriteLine(Server.GetClientName() + " -> " + DataRequestSet.GetTypeString(_lastRequest), InvokeMessageType.DataRequest); string msg = ""; User user = Factory.GetUser(); + + string username = ""; + string password = ""; + string autokey = ""; + Guid key = Guid.Empty; if (requestData.Count >= 4) { - ServerHelper.WriteLine(Server.GetClientName() + " -> " + DataRequestSet.GetTypeString(_lastRequest), InvokeMessageType.DataRequest); - string username = DataRequest.GetDictionaryJsonObject(requestData, "username") ?? ""; - string password = DataRequest.GetDictionaryJsonObject(requestData, "password") ?? ""; - string autokey = DataRequest.GetDictionaryJsonObject(requestData, "autokey") ?? ""; - Guid key = DataRequest.GetDictionaryJsonObject(requestData, "key"); + username = DataRequest.GetDictionaryJsonObject(requestData, "username") ?? ""; + password = DataRequest.GetDictionaryJsonObject(requestData, "password") ?? ""; + autokey = DataRequest.GetDictionaryJsonObject(requestData, "autokey") ?? ""; + key = DataRequest.GetDictionaryJsonObject(requestData, "key"); + } + else + { + ServerHelper.WriteLine("客户端提供的参数不足。", InvokeMessageType.DataRequest, LogLevel.Warning); + } - // CheckLogin的情况 - if (key != Guid.Empty) + LoginEventArgs eventArgs = new(username, password, autokey); + FunGameSystem.ServerPluginLoader?.OnBeforeLoginEvent(this, eventArgs); + FunGameSystem.WebAPIPluginLoader?.OnBeforeLoginEvent(this, eventArgs); + if (eventArgs.Cancel) + { + msg = $"{DataRequestSet.GetTypeString(_lastRequest)} 请求已取消。{(eventArgs.EventMsg != "" ? $"原因:{eventArgs.EventMsg}" : "")}"; + ServerHelper.WriteLine(msg); + resultData.Add("msg", msg); + resultData.Add("user", user); + return; + } + + // CheckLogin的情况 + if (key != Guid.Empty) + { + if (Server.IsLoginKey(key)) { - if (IsLoginKey(key)) - { - await CheckLogin(); - user = Server.User; - } - else ServerHelper.WriteLine("客户端发送了错误的秘钥,不允许本次登录。"); + await Server.CheckLogin(); + user = Server.User; } else { - // 验证登录 - if (username != null && password != null) - { - password = password.Encrypt(username); - ServerHelper.WriteLine("[" + DataRequestSet.GetTypeString(DataRequestType.Login_Login) + "] Username: " + username); - if (SQLHelper != null) - { - SQLHelper.ExecuteDataSet(UserQuery.Select_Users_LoginQuery(SQLHelper, username, password)); - if (SQLHelper.Result == SQLResult.Success) - { - DataSet dsUser = SQLHelper.DataSet; - if (autokey.Trim() != "") - { - SQLHelper.ExecuteDataSet(UserQuery.Select_CheckAutoKey(SQLHelper, username, autokey)); - if (SQLHelper.Result == SQLResult.Success) - { - ServerHelper.WriteLine("[" + DataRequestSet.GetTypeString(DataRequestType.Login_Login) + "] AutoKey: 已确认"); - } - else - { - msg = "AutoKey不正确,拒绝自动登录!"; - ServerHelper.WriteLine("[" + DataRequestSet.GetTypeString(DataRequestType.Login_Login) + "] " + msg); - } - } - key = Guid.NewGuid(); - PreLogin(dsUser, username, key); - resultData.Add("key", key); - } - else - { - msg = "用户名或密码不正确。"; - ServerHelper.WriteLine(msg); - } - } - } + msg = "客户端发送了错误的秘钥,不允许本次登录。"; + ServerHelper.WriteLine(msg, InvokeMessageType.DataRequest, LogLevel.Warning); } } + else + { + // 进行预登录 + (bool success, DataSet dsUser, msg, key) = DataRequestService.PreLogin(this, username, password, autokey); + if (success) + { + Server.PreLogin(dsUser, key); + resultData.Add("key", key); + } + } + + FunGameSystem.ServerPluginLoader?.OnAfterLoginEvent(this, eventArgs); + FunGameSystem.WebAPIPluginLoader?.OnAfterLoginEvent(this, eventArgs); resultData.Add("msg", msg); resultData.Add("user", user); } - /// - /// 预登录 - /// - /// - /// - /// - private void PreLogin(DataSet dsuser, string username, Guid checkloginkey) - { - _dsUser = dsuser; - _username = username; - _checkLoginKey = checkloginkey; - } - - /// - /// 确认登录 - /// - private async Task CheckLogin() - { - // 创建User对象 - Server.User = Factory.GetUser(_dsUser); - // 检查有没有重复登录的情况 - await Server.ForceLogOutDuplicateLogonUser(); - // 添加至玩家列表 - Server.AddUser(); - Server.GetUsersCount(); - } - - /// - /// 检查LoginKey - /// - /// - /// - private bool IsLoginKey(Guid key) - { - return key == _checkLoginKey; - } - /// /// 接收并验证找回密码时的验证码 /// @@ -773,7 +738,8 @@ namespace Milimoe.FunGame.Server.Controller string password = DataRequest.GetDictionaryJsonObject(requestData, UserQuery.Column_Password) ?? ""; if (username.Trim() != "" && password.Trim() != "") { - password = password.Encrypt(username); + FunGameSystem.UpdateUserKey(username); + password = password.Encrypt(FunGameSystem.GetUserKey(username)); SQLHelper?.Execute(UserQuery.Update_Password(SQLHelper, username, password)); if (SQLHelper?.Success ?? false) { @@ -802,7 +768,7 @@ namespace Milimoe.FunGame.Server.Controller ServerHelper.WriteLine(Server.GetClientName() + " -> " + DataRequestSet.GetTypeString(_lastRequest), InvokeMessageType.DataRequest); roomid = DataRequest.GetDictionaryJsonObject(requestData, "roomid") ?? "-1"; } - resultData.Add("count", Config.RoomList.GetUserCount(roomid)); + resultData.Add("count", FunGameSystem.RoomList.GetUserCount(roomid)); } /// @@ -863,11 +829,11 @@ namespace Milimoe.FunGame.Server.Controller List targets; if (roomtype == RoomType.All) { - targets = [.. Config.RoomList.ListRoom.Where(r => r.RoomState == RoomState.Created || r.RoomState == RoomState.Matching)]; + targets = [.. FunGameSystem.RoomList.ListRoom.Where(r => r.RoomState == RoomState.Created || r.RoomState == RoomState.Matching)]; } else { - targets = [.. Config.RoomList.ListRoom.Where(r => (r.RoomState == RoomState.Created || r.RoomState == RoomState.Matching) && r.RoomType == roomtype)]; + targets = [.. FunGameSystem.RoomList.ListRoom.Where(r => (r.RoomState == RoomState.Created || r.RoomState == RoomState.Matching) && r.RoomType == roomtype)]; } // 如果匹配停止,则退出 @@ -876,7 +842,7 @@ namespace Milimoe.FunGame.Server.Controller foreach (Room room in targets) { // 获取当前房间的玩家列表 - List players = Config.RoomList.GetUsers(room.Roomid); + List players = FunGameSystem.RoomList.GetUsers(room.Roomid); if (players.Count > 0) { // 计算房间平均Elo diff --git a/FunGame.Server/Main.cs b/FunGame.Server/Main.cs index 996c473..665eb49 100644 --- a/FunGame.Server/Main.cs +++ b/FunGame.Server/Main.cs @@ -105,6 +105,9 @@ void StartServer() ServerHelper.WriteLine("请输入 help 来获取帮助,按下 Ctrl+C 关闭服务器。"); + // 初始化用户密钥列表 + FunGameSystem.InitUserKeys(); + ServerHelper.PrintFunGameTitle(); // 使用Socket还是WebSocket diff --git a/FunGame.Server/Models/ConsoleModel.cs b/FunGame.Server/Models/ConsoleModel.cs index 7385874..411510f 100644 --- a/FunGame.Server/Models/ConsoleModel.cs +++ b/FunGame.Server/Models/ConsoleModel.cs @@ -52,16 +52,16 @@ namespace Milimoe.FunGame.Server.Model break; } // 广播到插件 - if (Config.ServerPluginLoader != null) + if (FunGameSystem.ServerPluginLoader != null) { - foreach (ServerPlugin plugin in Config.ServerPluginLoader.Plugins.Values) + foreach (ServerPlugin plugin in FunGameSystem.ServerPluginLoader.Plugins.Values) { plugin.ProcessInput(order); } } - if (Config.WebAPIPluginLoader != null) + if (FunGameSystem.WebAPIPluginLoader != null) { - foreach (WebAPIPlugin plugin in Config.WebAPIPluginLoader.Plugins.Values) + foreach (WebAPIPlugin plugin in FunGameSystem.WebAPIPluginLoader.Plugins.Values) { plugin.ProcessInput(order); } diff --git a/FunGame.Server/Models/ServerModel.cs b/FunGame.Server/Models/ServerModel.cs index a370363..6ad2c27 100644 --- a/FunGame.Server/Models/ServerModel.cs +++ b/FunGame.Server/Models/ServerModel.cs @@ -43,6 +43,8 @@ namespace Milimoe.FunGame.Server.Model protected string _username = ""; protected long _loginTime = 0; protected long _logoutTime = 0; + protected DataSet _dsUser = new(); + protected Guid _checkLoginKey = Guid.Empty; public ServerModel(ISocketListener server, ISocketMessageProcessor socket, bool isDebugMode) { @@ -259,9 +261,9 @@ namespace Milimoe.FunGame.Server.Model else { // 建立连接 - if (Config.GameModuleLoader != null && Config.GameModuleLoader.ModuleServers.ContainsKey(serverName)) + if (FunGameSystem.GameModuleLoader != null && FunGameSystem.GameModuleLoader.ModuleServers.ContainsKey(serverName)) { - GameModuleServer mod = Config.GameModuleLoader.GetServerMode(serverName); + GameModuleServer mod = FunGameSystem.GameModuleLoader.GetServerMode(serverName); if (mod.StartAnonymousServer(this, data)) { NowGamingServer = mod; @@ -381,13 +383,13 @@ namespace Milimoe.FunGame.Server.Model { bool result; - Config.RoomList.CancelReady(roomid, User); - Config.RoomList.QuitRoom(roomid, User); - Room Room = Config.RoomList[roomid] ?? General.HallInstance; + FunGameSystem.RoomList.CancelReady(roomid, User); + FunGameSystem.RoomList.QuitRoom(roomid, User); + Room Room = FunGameSystem.RoomList[roomid] ?? General.HallInstance; // 是否是房主 if (isMaster) { - List users = [.. Config.RoomList[roomid].UserAndIsReady.Keys]; + List users = [.. FunGameSystem.RoomList[roomid].UserAndIsReady.Keys]; if (users.Count > 0) // 如果此时房间还有人,更新房主 { User NewMaster = users[0]; @@ -399,7 +401,7 @@ namespace Milimoe.FunGame.Server.Model } else // 没人了就解散房间 { - Config.RoomList.RemoveRoom(roomid); + FunGameSystem.RoomList.RemoveRoom(roomid); SQLHelper?.Execute(RoomQuery.Delete_QuitRoom(SQLHelper, roomid, User.Id)); this.InRoom = General.HallInstance; ServerHelper.WriteLine("[ " + GetClientName() + " ] 解散了房间 " + roomid); @@ -421,10 +423,13 @@ namespace Milimoe.FunGame.Server.Model { foreach (IServerModel Client in Listener.ClientList.Where(c => c != null && c.User.Id != 0 && room.Roomid == c.InRoom?.Roomid)) { - await Client.Send(SocketMessageType.Chat, User.Username, DateTimeUtility.GetNowShortTime() + " [ " + User.Username + " ] 离开了房间。"); - if (isUpdateRoomMaster && room.RoomMaster?.Id != 0 && room.Roomid != "-1") + if (room.Roomid != "-1") { - await Client.Send(SocketMessageType.UpdateRoomMaster, room); + await Client.Send(SocketMessageType.Chat, User.Username, DateTimeUtility.GetNowShortTime() + " [ " + User.Username + " ] 离开了房间。"); + if (isUpdateRoomMaster && room.RoomMaster?.Id != 0) + { + await Client.Send(SocketMessageType.UpdateRoomMaster, room); + } } } } @@ -453,11 +458,28 @@ namespace Milimoe.FunGame.Server.Model if (Listener.UserList.ContainsKey(user)) { ServerHelper.WriteLine("OnlinePlayers: 玩家 " + user + " 重复登录!"); - await ForceLogOut("您的账号在别处登录,已强制下线。"); + await ((ServerModel)Listener.UserList[user]).ForceLogOut("您的账号在别处登录,已强制下线。"); } } } + public void PreLogin(DataSet dsuser, Guid checkloginkey) + { + _dsUser = dsuser; + _checkLoginKey = checkloginkey; + } + + public async Task CheckLogin() + { + // 创建User对象 + User = Factory.GetUser(_dsUser); + // 检查有没有重复登录的情况 + await ForceLogOutDuplicateLogonUser(); + // 添加至玩家列表 + AddUser(); + GetUsersCount(); + } + public bool AddUser() { if (User.Id != 0 && this != null) @@ -477,6 +499,7 @@ namespace Milimoe.FunGame.Server.Model { if (User.Id != 0 && this != null) { + _checkLoginKey = Guid.Empty; _logoutTime = DateTime.Now.Ticks; int TotalMinutes = Convert.ToInt32((new DateTime(_logoutTime) - new DateTime(_loginTime)).TotalMinutes); SQLHelper?.Execute(UserQuery.Update_GameTime(SQLHelper, User.Username, TotalMinutes)); @@ -501,6 +524,11 @@ namespace Milimoe.FunGame.Server.Model ServerHelper.WriteLine($"{Listener.Name} 的目前在线客户端数量: {Listener.ClientList.Count}(已登录的玩家数量:{Listener.UserList.Count})"); } + public bool IsLoginKey(Guid key) + { + return key == _checkLoginKey; + } + protected virtual async Task Read(ISocketMessageProcessor socket) { // 接收客户端消息 diff --git a/FunGame.Server/Others/Config.cs b/FunGame.Server/Others/Config.cs index 9bffd3d..940dbf0 100644 --- a/FunGame.Server/Others/Config.cs +++ b/FunGame.Server/Others/Config.cs @@ -139,35 +139,10 @@ namespace Milimoe.FunGame.Server.Others public static FunGameInfo.FunGame FunGameType => FunGameInfo.FunGame.FunGame_Server; /// - /// 服务器指令列表 - /// - public static Hashtable OrderList { get; } = []; - - /// - /// 在线房间列表 - /// - public static RoomList RoomList { get; } = new(); - - /// - /// 是否运行数据库模式 + /// 运行的数据库模式 /// public static SQLMode SQLMode { get; set; } = SQLMode.None; - /// - /// Server实际安装的模组 - /// - public static GameModuleLoader? GameModuleLoader { get; set; } - - /// - /// Server插件 - /// - public static ServerPluginLoader? ServerPluginLoader { get; set; } - - /// - /// Web API插件 - /// - public static WebAPIPluginLoader? WebAPIPluginLoader { get; set; } - /// /// 未Loadmodules时,此属性表示至少需要安装的模组 /// diff --git a/FunGame.Server/Services/DataRequestService.cs b/FunGame.Server/Services/DataRequestService.cs index 5c244bc..24f07ed 100644 --- a/FunGame.Server/Services/DataRequestService.cs +++ b/FunGame.Server/Services/DataRequestService.cs @@ -1,5 +1,7 @@ -using Milimoe.FunGame.Core.Api.Transmittal; +using System.Data; +using Milimoe.FunGame.Core.Api.Transmittal; using Milimoe.FunGame.Core.Api.Utility; +using Milimoe.FunGame.Core.Library.Common.Event; using Milimoe.FunGame.Core.Library.Constant; using Milimoe.FunGame.Core.Library.SQLScript.Common; using Milimoe.FunGame.Core.Library.SQLScript.Entity; @@ -9,15 +11,25 @@ namespace Milimoe.FunGame.Server.Services { public class DataRequestService { - public static (string Msg, RegInvokeType RegInvokeType, bool Success) Reg(string username, string password, string email, string verifyCode, string clientIP = "", SQLHelper? sqlHelper = null, MailSender? mailSender = null) + public static (string Msg, RegInvokeType RegInvokeType, bool Success) Reg(object sender, string username, string password, string email, string verifyCode, string clientIP = "") { string msg = ""; RegInvokeType type = RegInvokeType.None; bool success = false; string clientName = ServerHelper.MakeClientName(clientIP); - sqlHelper ??= Factory.OpenFactory.GetSQLHelper(); - mailSender ??= Factory.OpenFactory.GetMailSender(); + RegisterEventArgs eventArgs = new(username, password, email); + FunGameSystem.ServerPluginLoader?.OnBeforeRegEvent(sender, eventArgs); + FunGameSystem.WebAPIPluginLoader?.OnBeforeRegEvent(sender, eventArgs); + if (eventArgs.Cancel) + { + msg = $"{DataRequestSet.GetTypeString(DataRequestType.Reg_Reg)} 请求已取消。{(eventArgs.EventMsg != "" ? $"原因:{eventArgs.EventMsg}" : "")}"; + ServerHelper.WriteLine(msg, InvokeMessageType.DataRequest, LogLevel.Warning); + return (eventArgs.EventMsg, RegInvokeType.None, false); + } + + using SQLHelper? sqlHelper = Factory.OpenFactory.GetSQLHelper(); + using MailSender? mailSender = Factory.OpenFactory.GetMailSender(); if (sqlHelper != null) { // 如果没发验证码,就生成验证码 @@ -114,6 +126,8 @@ namespace Milimoe.FunGame.Server.Services { sqlHelper.NewTransaction(); ServerHelper.WriteLine("[Reg] Username: " + username + " Email: " + email); + FunGameSystem.UpdateUserKey(username); + password = password.Encrypt(FunGameSystem.GetUserKey(username)); sqlHelper.Execute(UserQuery.Insert_Register(sqlHelper, username, password, email, clientIP)); if (sqlHelper.Result == SQLResult.Success) { @@ -136,7 +150,69 @@ namespace Milimoe.FunGame.Server.Services } } + eventArgs.Success = success; + FunGameSystem.ServerPluginLoader?.OnAfterRegEvent(sender, eventArgs); + FunGameSystem.WebAPIPluginLoader?.OnAfterRegEvent(sender, eventArgs); + return (msg, type, success); } + + public static (bool Success, DataSet DataSet, string Msg, Guid Key) PreLogin(object sender, string username, string password, string autokey = "") + { + bool success = false; + DataSet dsUser = new(); + string msg = "用户名或密码不正确。"; + Guid key = Guid.Empty; + + LoginEventArgs eventArgs = new(username, password, autokey); + FunGameSystem.ServerPluginLoader?.OnBeforeLoginEvent(sender, eventArgs); + FunGameSystem.WebAPIPluginLoader?.OnBeforeLoginEvent(sender, eventArgs); + if (eventArgs.Cancel) + { + msg = $"{DataRequestSet.GetTypeString(DataRequestType.Login_Login)} 请求已取消。{(eventArgs.EventMsg != "" ? $"原因:{eventArgs.EventMsg}" : "")}"; + ServerHelper.WriteLine(msg, InvokeMessageType.DataRequest, LogLevel.Warning); + return (success, dsUser, eventArgs.EventMsg, key); + } + + // 验证登录 + if (username != "" && password != "") + { + password = password.Encrypt(FunGameSystem.GetUserKey(username)); + ServerHelper.WriteLine("[" + DataRequestSet.GetTypeString(DataRequestType.Login_Login) + "] Username: " + username); + using SQLHelper? sqlHelper = Factory.OpenFactory.GetSQLHelper(); + if (sqlHelper != null) + { + sqlHelper.ExecuteDataSet(UserQuery.Select_Users_LoginQuery(sqlHelper, username, password)); + if (sqlHelper.Result == SQLResult.Success) + { + dsUser = sqlHelper.DataSet; + key = Guid.NewGuid(); + success = true; + msg = ""; + if (autokey.Trim() != "") + { + sqlHelper.ExecuteDataSet(UserQuery.Select_CheckAutoKey(sqlHelper, username, autokey)); + if (sqlHelper.Result == SQLResult.Success) + { + ServerHelper.WriteLine("[" + DataRequestSet.GetTypeString(DataRequestType.Login_Login) + "] AutoKey: 已确认"); + } + else + { + success = false; + msg = "AutoKey 不正确,拒绝自动登录!"; + ServerHelper.WriteLine("[" + DataRequestSet.GetTypeString(DataRequestType.Login_Login) + "] " + msg); + } + } + } + } + } + + eventArgs.Success = success; + FunGameSystem.ServerPluginLoader?.OnAfterLoginEvent(sender, eventArgs); + FunGameSystem.WebAPIPluginLoader?.OnAfterLoginEvent(sender, eventArgs); + + ServerHelper.WriteLine(msg, InvokeMessageType.Core); + return (success, dsUser, msg, key); + } } } diff --git a/FunGame.Server/Services/MySQL/MySQLHelper.cs b/FunGame.Server/Services/DataUtility/MySQLHelper.cs similarity index 93% rename from FunGame.Server/Services/MySQL/MySQLHelper.cs rename to FunGame.Server/Services/DataUtility/MySQLHelper.cs index fec89eb..851c4b6 100644 --- a/FunGame.Server/Services/MySQL/MySQLHelper.cs +++ b/FunGame.Server/Services/DataUtility/MySQLHelper.cs @@ -3,10 +3,9 @@ using Milimoe.FunGame.Core.Api.Transmittal; using Milimoe.FunGame.Core.Library.Constant; using Milimoe.FunGame.Core.Model; using Milimoe.FunGame.Server.Models; -using Milimoe.FunGame.Server.Services; using MySql.Data.MySqlClient; -namespace Milimoe.FunGame.Server.DataUtility +namespace Milimoe.FunGame.Server.Services.DataUtility { public class MySQLHelper : SQLHelper { @@ -187,6 +186,29 @@ namespace Milimoe.FunGame.Server.DataUtility return _dataSet; } + /// + /// 检查数据库是否存在 + /// + /// + public override bool DatabaseExists() + { + try + { + OpenConnection(); + return true; + } + catch (Exception e) + { + ServerHelper.Error(e); + _result = SQLResult.Fail; + return false; + } + finally + { + Close(); + } + } + /// /// 创建一个SQL事务 /// diff --git a/FunGame.Server/Services/SQLite/SQLiteHelper.cs b/FunGame.Server/Services/DataUtility/SQLiteHelper.cs similarity index 89% rename from FunGame.Server/Services/SQLite/SQLiteHelper.cs rename to FunGame.Server/Services/DataUtility/SQLiteHelper.cs index 4f60718..ac16352 100644 --- a/FunGame.Server/Services/SQLite/SQLiteHelper.cs +++ b/FunGame.Server/Services/DataUtility/SQLiteHelper.cs @@ -4,9 +4,8 @@ using Milimoe.FunGame.Core.Api.Transmittal; using Milimoe.FunGame.Core.Library.Constant; using Milimoe.FunGame.Core.Model; using Milimoe.FunGame.Server.Models; -using Milimoe.FunGame.Server.Services; -namespace Milimoe.FunGame.Server.DataUtility +namespace Milimoe.FunGame.Server.Services.DataUtility { public class SQLiteHelper : SQLHelper { @@ -244,35 +243,11 @@ namespace Milimoe.FunGame.Server.DataUtility /// 检查数据库是否存在 /// /// - public bool DatabaseExists() + public override bool DatabaseExists() { return File.Exists(ServerInfo.SQLServerDataBase); } - /// - /// 执行SQL文件中的所有SQL语句来初始化数据库 - /// - /// SQL文件路径 - public void ExecuteSqlFile(string sqlFilePath) - { - if (!File.Exists(sqlFilePath)) - { - throw new FileNotFoundException("SQL文件不存在", sqlFilePath); - } - - string sqlContent = File.ReadAllText(sqlFilePath); - string[] sqlCommands = sqlContent.Split([";"], StringSplitOptions.RemoveEmptyEntries); - - foreach (string commandText in sqlCommands) - { - string sql = commandText.Trim(); - if (!string.IsNullOrEmpty(sql)) - { - Execute(sql); - } - } - } - private bool _isDisposed = false; /// diff --git a/FunGame.Server/Services/FunGameSystem.cs b/FunGame.Server/Services/FunGameSystem.cs index 2fa68f8..9190a0f 100644 --- a/FunGame.Server/Services/FunGameSystem.cs +++ b/FunGame.Server/Services/FunGameSystem.cs @@ -1,16 +1,53 @@ -using Milimoe.FunGame.Core.Api.Transmittal; +using System.Collections; +using Milimoe.FunGame.Core.Api.Transmittal; using Milimoe.FunGame.Core.Api.Utility; using Milimoe.FunGame.Core.Library.Common.Addon; using Milimoe.FunGame.Core.Library.Constant; using Milimoe.FunGame.Core.Library.SQLScript.Common; using Milimoe.FunGame.Core.Library.SQLScript.Entity; -using Milimoe.FunGame.Server.DataUtility; +using Milimoe.FunGame.Core.Model; using Milimoe.FunGame.Server.Others; +using Milimoe.FunGame.Server.Services.DataUtility; namespace Milimoe.FunGame.Server.Services { public class FunGameSystem { + /// + /// 服务器指令列表 + /// + public static Hashtable OrderList { get; } = []; + + /// + /// 在线房间列表 + /// + public static RoomList RoomList { get; } = new(); + + /// + /// Server实际安装的模组 + /// + public static GameModuleLoader? GameModuleLoader { get; set; } + + /// + /// Server插件 + /// + public static ServerPluginLoader? ServerPluginLoader { get; set; } + + /// + /// Web API插件 + /// + public static WebAPIPluginLoader? WebAPIPluginLoader { get; set; } + + /// + /// 服务器配置 + /// + public static PluginConfig UserKeys { get; set; } = new("system", "user_keys"); + + /// + /// FunGame Web API Token ID + /// + public const string FunGameWebAPITokenID = "fungame_web_api"; + /// /// 初始化数据库连接器 /// @@ -98,14 +135,14 @@ namespace Milimoe.FunGame.Server.Services // 读取modules目录下的模组 try { - Config.GameModuleLoader = GameModuleLoader.LoadGameModules(Config.FunGameType, delegates); - foreach (GameModuleServer module in Config.GameModuleLoader.ModuleServers.Values) + GameModuleLoader = GameModuleLoader.LoadGameModules(Config.FunGameType, delegates); + foreach (GameModuleServer module in GameModuleLoader.ModuleServers.Values) { try { bool check = true; // 检查模组是否有相对应的地图 - if (!Config.GameModuleLoader.Maps.ContainsKey(module.DefaultMap)) + if (!GameModuleLoader.Maps.ContainsKey(module.DefaultMap)) { ServerHelper.WriteLine("GameModule Load Failed: " + module.Name + " 没有找到相对应的地图,加载失败", InvokeMessageType.Error); check = false; @@ -127,7 +164,7 @@ namespace Milimoe.FunGame.Server.Services ServerHelper.Error(e2); } // 设置全局 - Config.GameModuleSupported = supported.Distinct().ToArray(); + Config.GameModuleSupported = [.. supported.Distinct()]; return Config.GameModuleSupported.Length > 0; } @@ -143,8 +180,8 @@ namespace Milimoe.FunGame.Server.Services try { // 读取plugins目录下的插件 - Config.ServerPluginLoader = ServerPluginLoader.LoadPlugins(delegates); - foreach (ServerPlugin plugin in Config.ServerPluginLoader.Plugins.Values) + ServerPluginLoader = ServerPluginLoader.LoadPlugins(delegates); + foreach (ServerPlugin plugin in ServerPluginLoader.Plugins.Values) { ServerHelper.WriteLine("Plugin Loaded -> " + plugin.Name, InvokeMessageType.Core); } @@ -166,8 +203,8 @@ namespace Milimoe.FunGame.Server.Services try { // 读取plugins目录下的插件 - Config.WebAPIPluginLoader = WebAPIPluginLoader.LoadPlugins(delegates, otherobjs); - foreach (WebAPIPlugin plugin in Config.WebAPIPluginLoader.Plugins.Values) + WebAPIPluginLoader = WebAPIPluginLoader.LoadPlugins(delegates, otherobjs); + foreach (WebAPIPlugin plugin in WebAPIPluginLoader.Plugins.Values) { ServerHelper.WriteLine("Plugin Loaded -> " + plugin.Name, InvokeMessageType.Core); } @@ -194,6 +231,87 @@ namespace Milimoe.FunGame.Server.Services sqlHelper.Execute(RoomQuery.Delete_Rooms(sqlHelper)); } + /// + /// 初始化用户密钥列表 + /// + public static void InitUserKeys() + { + UserKeys.LoadConfig(); + UserKeys.SaveConfig(); + } + + /// + /// 获取指定用户的密钥 + /// + /// + /// + public static string GetUserKey(string username) + { + if (UserKeys.TryGetValue(username.ToLower(), out object? value) && value is string key) + { + return key; + } + return username; + } + + /// + /// 更新指定用户的密钥 + /// + /// + /// + public static void UpdateUserKey(string username) + { + UserKeys.Add(username.ToLower(), Encryption.GenerateRandomString()); + UserKeys.SaveConfig(); + } + + /// + /// 获取 API Secret Key + /// + /// + public static string GetAPISecretKey(string token) + { + using SQLHelper? sql = Factory.OpenFactory.GetSQLHelper(); + if (sql != null) + { + sql.ExecuteDataSet(ApiTokens.Select_GetAPIToken(sql, token)); + if (sql.Result == SQLResult.Success) + { + return sql.DataSet.Tables[0].Rows[0][ApiTokens.Column_SecretKey].ToString() ?? ""; + } + } + return ""; + } + + /// + /// 设置 API Secret Key + /// + /// + /// + /// + public static void SetAPISecretKey(string token, string reference1 = "", string reference2 = "", SQLHelper? sqlHelper = null) + { + bool useSQLHelper = sqlHelper != null; + sqlHelper ??= Factory.OpenFactory.GetSQLHelper(); + string key = Encryption.GenerateRandomString(); + if (sqlHelper != null) + { + sqlHelper.ExecuteDataSet(ApiTokens.Select_GetAPIToken(sqlHelper, token)); + if (sqlHelper.Success) + { + sqlHelper.Execute(ApiTokens.Update_GetAPIToken(sqlHelper, token, key, reference1, reference2)); + } + else + { + sqlHelper.Execute(ApiTokens.Insert_APITokens(sqlHelper, token, key, reference1, reference2)); + } + } + if (!useSQLHelper) + { + sqlHelper?.Dispose(); + } + } + /// /// 创建 SQL 服务后需要做的事 /// @@ -201,10 +319,18 @@ namespace Milimoe.FunGame.Server.Services public static void AfterCreateSQLService(SQLHelper sqlHelper) { Config.SQLMode = sqlHelper.Mode; - if (sqlHelper is SQLiteHelper sqliteHelper && !sqliteHelper.DatabaseExists()) + if (!sqlHelper.DatabaseExists()) { ServerHelper.WriteLine("正在初始化数据库 . . .", InvokeMessageType.Core); - sqliteHelper.ExecuteSqlFile(AppDomain.CurrentDomain.BaseDirectory + "fungame_sqlite.sql"); + if (sqlHelper is SQLiteHelper sqliteHelper) + { + sqliteHelper.ExecuteSqlFile(AppDomain.CurrentDomain.BaseDirectory + "fungame_sqlite.sql"); + } + else if (sqlHelper is MySQLHelper mysqlHelper) + { + mysqlHelper.ExecuteSqlFile(AppDomain.CurrentDomain.BaseDirectory + "fungame.sql"); + } + SetAPISecretKey(FunGameWebAPITokenID, sqlHelper: sqlHelper); } ServerLogin(sqlHelper); ClearRoomList(sqlHelper); @@ -216,23 +342,23 @@ namespace Milimoe.FunGame.Server.Services /// public static void CloseServer() { - if (Config.GameModuleLoader != null) + if (GameModuleLoader != null) { - foreach (GameModuleServer server in Config.GameModuleLoader.ModuleServers.Values) + foreach (GameModuleServer server in GameModuleLoader.ModuleServers.Values) { server.Controller.Close(); } } - if (Config.ServerPluginLoader != null) + if (ServerPluginLoader != null) { - foreach (ServerPlugin plugin in Config.ServerPluginLoader.Plugins.Values) + foreach (ServerPlugin plugin in ServerPluginLoader.Plugins.Values) { plugin.Controller.Close(); } } - if (Config.WebAPIPluginLoader != null) + if (WebAPIPluginLoader != null) { - foreach (WebAPIPlugin plugin in Config.WebAPIPluginLoader.Plugins.Values) + foreach (WebAPIPlugin plugin in WebAPIPluginLoader.Plugins.Values) { plugin.Controller.Close(); } diff --git a/FunGame.Server/Services/General.cs b/FunGame.Server/Services/General.cs index 81f7a55..462596b 100644 --- a/FunGame.Server/Services/General.cs +++ b/FunGame.Server/Services/General.cs @@ -233,12 +233,12 @@ namespace Milimoe.FunGame.Server.Services public static void InitOrderList() { - Config.OrderList.Clear(); - Config.OrderList.Add(OrderDictionary.Help, "Milimoe -> 帮助"); - Config.OrderList.Add(OrderDictionary.Quit, "关闭服务器"); - Config.OrderList.Add(OrderDictionary.Exit, "关闭服务器"); - Config.OrderList.Add(OrderDictionary.Close, "关闭服务器"); - Config.OrderList.Add(OrderDictionary.Restart, "重启服务器"); + FunGameSystem.OrderList.Clear(); + FunGameSystem.OrderList.Add(OrderDictionary.Help, "Milimoe -> 帮助"); + FunGameSystem.OrderList.Add(OrderDictionary.Quit, "关闭服务器"); + FunGameSystem.OrderList.Add(OrderDictionary.Exit, "关闭服务器"); + FunGameSystem.OrderList.Add(OrderDictionary.Close, "关闭服务器"); + FunGameSystem.OrderList.Add(OrderDictionary.Restart, "重启服务器"); } } diff --git a/FunGame.WebAPI/Controllers/UserController.cs b/FunGame.WebAPI/Controllers/UserController.cs index b4406c8..560cb90 100644 --- a/FunGame.WebAPI/Controllers/UserController.cs +++ b/FunGame.WebAPI/Controllers/UserController.cs @@ -1,9 +1,8 @@ -using System.Security.Claims; +using System.Data; +using System.Security.Claims; using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Mvc; -using Milimoe.FunGame.Core.Api.Utility; using Milimoe.FunGame.Core.Library.Constant; -using Milimoe.FunGame.Core.Library.SQLScript.Entity; using Milimoe.FunGame.Server.Others; using Milimoe.FunGame.Server.Services; using Milimoe.FunGame.WebAPI.Architecture; @@ -17,19 +16,21 @@ namespace Milimoe.FunGame.WebAPI.Controllers public class UserController(RESTfulAPIListener apiListener, JWTService jwtTokenService, ILogger logger) : ControllerBase { [HttpPost("reg")] + [Authorize(AuthenticationSchemes = "APIBearer")] public IActionResult Reg([FromBody] RegDTO dto) { + // 因为注册 API 不需要先登录,所以需要进行 API Bearer Token 验证,防止 API 滥用 + // API Bearer Token 保存在数据库 apitokens 表中,由服务器管理员配置 try { string clientIP = HttpContext.Connection.RemoteIpAddress?.ToString() + ":" + HttpContext.Connection.RemotePort; ServerHelper.WriteLine(ServerHelper.MakeClientName(clientIP) + " 通过 RESTful API 注册账号 . . .", InvokeMessageType.Core); - string username = dto.Username; string password = dto.Password; string email = dto.Email; string verifycode = dto.VerifyCode; - (string msg, RegInvokeType type, bool success) = DataRequestService.Reg(username, password, email, verifycode, clientIP); + (string msg, RegInvokeType type, bool success) = DataRequestService.Reg(apiListener, username, password, email, verifycode, clientIP); return Ok(new PayloadModel() { @@ -53,69 +54,85 @@ namespace Milimoe.FunGame.WebAPI.Controllers [HttpPost("login")] public async Task Login([FromBody] LoginDTO dto) { + Config.ConnectingPlayerCount++; try { PayloadModel response = new() { RequestType = DataRequestType.Login_Login }; - string msg = "用户名或密码不正确。"; + string msg = "服务器暂时无法处理登录请求。"; string clientIP = HttpContext.Connection.RemoteIpAddress?.ToString() + ":" + HttpContext.Connection.RemotePort; ServerHelper.WriteLine(ServerHelper.MakeClientName(clientIP) + " 通过 RESTful API 连接至服务器,正在登录 . . .", InvokeMessageType.Core); string username = dto.Username; string password = dto.Password; - if (apiListener != null) + RESTfulAPIModel? model = await CheckConnection(username, clientIP); + if (model != null) { - // 移除旧模型 - if (apiListener.UserList.ContainsKey(username)) + // 预登录 + (bool success, DataSet dsUser, msg, Guid key) = DataRequestService.PreLogin(this, username, password); + if (success) { - await apiListener.UserList[username].Send(SocketMessageType.Disconnect); - } - // 创建新模型 - if (!apiListener.UserList.ContainsKey(username)) - { - Config.ConnectingPlayerCount++; - RESTfulAPIModel model = new(apiListener, clientIP); - model.SetClientName(clientIP); - // 创建User对象 - if (model.SQLHelper != null) + model.PreLogin(dsUser, key); + // 确认登录 + await model.CheckLogin(); + string token = jwtTokenService.GenerateToken(username); + Config.ConnectingPlayerCount--; + response.StatusCode = 200; + response.Message = "登录成功!"; + response.Data = new() { - model.SQLHelper.ExecuteDataSet(UserQuery.Select_Users_LoginQuery(model.SQLHelper, username, password)); - Core.Entity.User user = Factory.GetUser(model.SQLHelper?.DataSet ?? new()); - if (user.Id != 0) - { - model.User = user; - // 检查有没有重复登录的情况 - await model.ForceLogOutDuplicateLogonUser(); - // 添加至玩家列表 - model.AddUser(); - model.GetUsersCount(); - string token = jwtTokenService.GenerateToken(username); - Config.ConnectingPlayerCount--; - response.StatusCode = 200; - response.Message = "登录成功!"; - response.Data = new() - { - { "bearerToken", token }, - { "openToken", model.Token } - }; - return Ok(response); - } - } - else msg = "服务器暂时无法处理登录请求。"; - await model.Send(SocketMessageType.Disconnect); + { "bearerToken", token }, + { "openToken", model.Token } + }; + return Ok(response); } + await model.Send(SocketMessageType.Disconnect); } Config.ConnectingPlayerCount--; - ServerHelper.WriteLine(msg, InvokeMessageType.Core); response.Message = msg; response.StatusCode = 401; return Unauthorized(response); } catch (Exception e) + { + Config.ConnectingPlayerCount--; + logger.LogError("Error: {e}", e); + } + return BadRequest(); + } + + [HttpPost("logout")] + [Authorize] + public async Task LogOut() + { + try + { + PayloadModel response = new() + { + RequestType = DataRequestType.RunTime_Logout + }; + string username = User.FindFirstValue(ClaimTypes.NameIdentifier) ?? ""; + + if (apiListener.UserList.ContainsKey(username)) + { + RESTfulAPIModel model = (RESTfulAPIModel)apiListener.UserList[username]; + await model.Send(SocketMessageType.Disconnect); + RevokeToken(); + model.GetUsersCount(); + response.Message = "你已成功退出登录! "; + response.StatusCode = 200; + return Ok(response); + } + + response.Message = "退出登录失败!"; + response.StatusCode = 400; + return BadRequest(response); + } + catch (Exception e) { logger.LogError("Error: {e}", e); } @@ -128,16 +145,12 @@ namespace Milimoe.FunGame.WebAPI.Controllers { try { - string oldToken = HttpContext.Request.Headers.Authorization.ToString().Replace("Bearer ", ""); - - // 吊销 - jwtTokenService.RevokeToken(oldToken); - - // 生成 + RevokeToken(); string username = User.FindFirstValue(ClaimTypes.NameIdentifier) ?? ""; - string newToken = jwtTokenService.GenerateToken(username); - - return Ok(newToken); + if (username.Trim() != "") + { + return Ok(jwtTokenService.GenerateToken(username)); + } } catch (Exception e) { @@ -145,5 +158,33 @@ namespace Milimoe.FunGame.WebAPI.Controllers } return BadRequest(); } + + private async Task CheckConnection(string username, string clientIP) + { + if (apiListener != null) + { + // 移除旧模型 + if (apiListener.UserList.ContainsKey(username)) + { + await apiListener.UserList[username].Send(SocketMessageType.Disconnect); + RevokeToken(); + } + // 创建新模型 + if (!apiListener.UserList.ContainsKey(username)) + { + RESTfulAPIModel model = new(apiListener, clientIP); + model.SetClientName(clientIP); + return model; + } + } + return null; + } + + private void RevokeToken() + { + // 吊销令牌 + string oldToken = HttpContext.Request.Headers.Authorization.ToString().Replace("Bearer ", ""); + if (oldToken.Trim() != "") jwtTokenService.RevokeToken(oldToken); + } } } diff --git a/FunGame.WebAPI/Program.cs b/FunGame.WebAPI/Program.cs index 5f4d1dd..9192c7b 100644 --- a/FunGame.WebAPI/Program.cs +++ b/FunGame.WebAPI/Program.cs @@ -62,6 +62,9 @@ try // 读取 Server 插件 FunGameSystem.GetServerPlugins(); + // 初始化用户密钥列表 + FunGameSystem.InitUserKeys(); + // Add services to the container. WebApplicationBuilder builder = WebApplication.CreateBuilder(args); @@ -92,9 +95,9 @@ try Console.WriteLine("\r "); // 读取扩展控制器 - if (Config.WebAPIPluginLoader != null) + if (FunGameSystem.WebAPIPluginLoader != null) { - foreach (WebAPIPlugin plugin in Config.WebAPIPluginLoader.Plugins.Values) + foreach (WebAPIPlugin plugin in FunGameSystem.WebAPIPluginLoader.Plugins.Values) { Assembly? pluginAssembly = Assembly.GetAssembly(plugin.GetType()); @@ -150,7 +153,8 @@ try IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(builder.Configuration["Jwt:Key"] ?? "undefined")), NameClaimType = ClaimTypes.NameIdentifier }; - }).AddScheme("CustomBearer", options => { }); + }).AddScheme("APIBearer", options => { }). + AddScheme("CustomBearer", options => { }); builder.Logging.AddConsole(options => { options.FormatterName = "CustomFormatter"; diff --git a/FunGame.WebAPI/Services/APIBearerTokenHandler.cs b/FunGame.WebAPI/Services/APIBearerTokenHandler.cs new file mode 100644 index 0000000..f78dd53 --- /dev/null +++ b/FunGame.WebAPI/Services/APIBearerTokenHandler.cs @@ -0,0 +1,54 @@ +using System.Security.Claims; +using System.Text.Encodings.Web; +using Microsoft.AspNetCore.Authentication; +using Microsoft.Extensions.Caching.Memory; +using Microsoft.Extensions.Options; +using Milimoe.FunGame.Server.Services; + +namespace Milimoe.FunGame.WebAPI.Services +{ + public class APIBearerAuthenticationHandler(IMemoryCache memoryCache, IOptionsMonitor options, ILoggerFactory logger, UrlEncoder encoder) : AuthenticationHandler(options, logger, encoder) + { + protected override async Task HandleAuthenticateAsync() + { + // 检查是否有 Authorization Header + if (!Request.Headers.TryGetValue("Authorization", out Microsoft.Extensions.Primitives.StringValues value)) + { + return AuthenticateResult.Fail("Authorization header is missing."); + } + + string authorizationHeader = value.ToString(); + if (!authorizationHeader.StartsWith("Bearer ", StringComparison.OrdinalIgnoreCase)) + { + return AuthenticateResult.Fail("Invalid Authorization header format."); + } + + string token = authorizationHeader["Bearer ".Length..].Trim(); + + // 验证 API Bearer Token + string key; + if (memoryCache.TryGetValue(FunGameSystem.FunGameWebAPITokenID, out object? cacheValue) && cacheValue is string str) + { + key = str; + } + else + { + key = FunGameSystem.GetAPISecretKey(FunGameSystem.FunGameWebAPITokenID); + memoryCache.Set(FunGameSystem.FunGameWebAPITokenID, key, TimeSpan.FromMinutes(5)); + } + if (key == "" || token != key) + { + await Task.CompletedTask; + return AuthenticateResult.Fail("Invalid Token."); + } + + // 如果验证通过,创建 ClaimsIdentity + Claim[] claims = [new Claim(ClaimTypes.Name, "FunGame Web API Claim")]; + ClaimsIdentity identity = new(claims, Scheme.Name); + ClaimsPrincipal principal = new(identity); + AuthenticationTicket ticket = new(principal, Scheme.Name); + + return AuthenticateResult.Success(ticket); + } + } +}