服务器结构调整;添加 APIBearer;修复强制下线的目标客户端错误的问题 (#46)

* 服务器结构调整;添加 APIBearer

* Server.exe 需要初始化用户密钥列表,UeerKeys 均需要小写用户名保存和读取

* 修复强制下线的目标客户端错误的问题
This commit is contained in:
milimoe 2025-03-17 00:49:58 +08:00 committed by GitHub
parent 5f8d8e5214
commit d35f9aa81a
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
13 changed files with 542 additions and 272 deletions

View File

@ -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;
/// <summary>
@ -150,12 +149,11 @@ namespace Milimoe.FunGame.Server.Controller
{
ServerHelper.WriteLine(Server.GetClientName() + " -> " + DataRequestSet.GetTypeString(_lastRequest), InvokeMessageType.DataRequest);
key = DataRequest.GetDictionaryJsonObject<Guid>(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<string>(requestData, "gamemap") ?? "";
bool isrank = DataRequest.GetDictionaryJsonObject<bool>(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<string, object> resultData)
{
ServerHelper.WriteLine(Server.GetClientName() + " -> " + DataRequestSet.GetTypeString(_lastRequest), InvokeMessageType.DataRequest);
resultData.Add("rooms", Config.RoomList.ListRoom); // 传RoomList
resultData.Add("rooms", FunGameSystem.RoomList.ListRoom); // 传RoomList
}
/// <summary>
@ -257,7 +255,7 @@ namespace Milimoe.FunGame.Server.Controller
string roomid = DataRequest.GetDictionaryJsonObject<string>(requestData, "roomid") ?? "-1";
bool isMaster = DataRequest.GetDictionaryJsonObject<bool>(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<string>(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));
}
/// <summary>
@ -370,15 +368,15 @@ namespace Milimoe.FunGame.Server.Controller
roomid = DataRequest.GetDictionaryJsonObject<string>(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));
}
/// <summary>
@ -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<User> users = Config.RoomList.GetUsers(roomid);
List<User> 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<string, IServerModel> all = Server.Listener.UserList.Cast<IServerModel>().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<string>(requestData, "password") ?? "";
string email = DataRequest.GetDictionaryJsonObject<string>(requestData, "email") ?? "";
string verifycode = DataRequest.GetDictionaryJsonObject<string>(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
/// <param name="resultData"></param>
private async Task Login(Dictionary<string, object> requestData, Dictionary<string, object> 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<string>(requestData, "username") ?? "";
string password = DataRequest.GetDictionaryJsonObject<string>(requestData, "password") ?? "";
string autokey = DataRequest.GetDictionaryJsonObject<string>(requestData, "autokey") ?? "";
Guid key = DataRequest.GetDictionaryJsonObject<Guid>(requestData, "key");
username = DataRequest.GetDictionaryJsonObject<string>(requestData, "username") ?? "";
password = DataRequest.GetDictionaryJsonObject<string>(requestData, "password") ?? "";
autokey = DataRequest.GetDictionaryJsonObject<string>(requestData, "autokey") ?? "";
key = DataRequest.GetDictionaryJsonObject<Guid>(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);
}
/// <summary>
/// 预登录
/// </summary>
/// <param name="dsuser"></param>
/// <param name="username"></param>
/// <param name="checkloginkey"></param>
private void PreLogin(DataSet dsuser, string username, Guid checkloginkey)
{
_dsUser = dsuser;
_username = username;
_checkLoginKey = checkloginkey;
}
/// <summary>
/// 确认登录
/// </summary>
private async Task CheckLogin()
{
// 创建User对象
Server.User = Factory.GetUser(_dsUser);
// 检查有没有重复登录的情况
await Server.ForceLogOutDuplicateLogonUser();
// 添加至玩家列表
Server.AddUser();
Server.GetUsersCount();
}
/// <summary>
/// 检查LoginKey
/// </summary>
/// <param name="key"></param>
/// <returns></returns>
private bool IsLoginKey(Guid key)
{
return key == _checkLoginKey;
}
/// <summary>
/// 接收并验证找回密码时的验证码
/// </summary>
@ -773,7 +738,8 @@ namespace Milimoe.FunGame.Server.Controller
string password = DataRequest.GetDictionaryJsonObject<string>(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<string>(requestData, "roomid") ?? "-1";
}
resultData.Add("count", Config.RoomList.GetUserCount(roomid));
resultData.Add("count", FunGameSystem.RoomList.GetUserCount(roomid));
}
/// <summary>
@ -863,11 +829,11 @@ namespace Milimoe.FunGame.Server.Controller
List<Room> 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<User> players = Config.RoomList.GetUsers(room.Roomid);
List<User> players = FunGameSystem.RoomList.GetUsers(room.Roomid);
if (players.Count > 0)
{
// 计算房间平均Elo

View File

@ -105,6 +105,9 @@ void StartServer()
ServerHelper.WriteLine("请输入 help 来获取帮助,按下 Ctrl+C 关闭服务器。");
// 初始化用户密钥列表
FunGameSystem.InitUserKeys();
ServerHelper.PrintFunGameTitle();
// 使用Socket还是WebSocket

View File

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

View File

@ -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<T> 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<User> users = [.. Config.RoomList[roomid].UserAndIsReady.Keys];
List<User> 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<T>)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<bool> Read(ISocketMessageProcessor socket)
{
// 接收客户端消息

View File

@ -139,35 +139,10 @@ namespace Milimoe.FunGame.Server.Others
public static FunGameInfo.FunGame FunGameType => FunGameInfo.FunGame.FunGame_Server;
/// <summary>
/// 服务器指令列表
/// </summary>
public static Hashtable OrderList { get; } = [];
/// <summary>
/// 在线房间列表
/// </summary>
public static RoomList RoomList { get; } = new();
/// <summary>
/// 是否运行数据库模式
/// 运行的数据库模式
/// </summary>
public static SQLMode SQLMode { get; set; } = SQLMode.None;
/// <summary>
/// Server实际安装的模组
/// </summary>
public static GameModuleLoader? GameModuleLoader { get; set; }
/// <summary>
/// Server插件
/// </summary>
public static ServerPluginLoader? ServerPluginLoader { get; set; }
/// <summary>
/// Web API插件
/// </summary>
public static WebAPIPluginLoader? WebAPIPluginLoader { get; set; }
/// <summary>
/// 未Loadmodules时此属性表示至少需要安装的模组
/// </summary>

View File

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

View File

@ -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;
}
/// <summary>
/// 检查数据库是否存在
/// </summary>
/// <returns></returns>
public override bool DatabaseExists()
{
try
{
OpenConnection();
return true;
}
catch (Exception e)
{
ServerHelper.Error(e);
_result = SQLResult.Fail;
return false;
}
finally
{
Close();
}
}
/// <summary>
/// 创建一个SQL事务
/// </summary>

View File

@ -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
/// 检查数据库是否存在
/// </summary>
/// <returns></returns>
public bool DatabaseExists()
public override bool DatabaseExists()
{
return File.Exists(ServerInfo.SQLServerDataBase);
}
/// <summary>
/// 执行SQL文件中的所有SQL语句来初始化数据库
/// </summary>
/// <param name="sqlFilePath">SQL文件路径</param>
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;
/// <summary>

View File

@ -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
{
/// <summary>
/// 服务器指令列表
/// </summary>
public static Hashtable OrderList { get; } = [];
/// <summary>
/// 在线房间列表
/// </summary>
public static RoomList RoomList { get; } = new();
/// <summary>
/// Server实际安装的模组
/// </summary>
public static GameModuleLoader? GameModuleLoader { get; set; }
/// <summary>
/// Server插件
/// </summary>
public static ServerPluginLoader? ServerPluginLoader { get; set; }
/// <summary>
/// Web API插件
/// </summary>
public static WebAPIPluginLoader? WebAPIPluginLoader { get; set; }
/// <summary>
/// 服务器配置
/// </summary>
public static PluginConfig UserKeys { get; set; } = new("system", "user_keys");
/// <summary>
/// FunGame Web API Token ID
/// </summary>
public const string FunGameWebAPITokenID = "fungame_web_api";
/// <summary>
/// 初始化数据库连接器
/// </summary>
@ -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));
}
/// <summary>
/// 初始化用户密钥列表
/// </summary>
public static void InitUserKeys()
{
UserKeys.LoadConfig();
UserKeys.SaveConfig();
}
/// <summary>
/// 获取指定用户的密钥
/// </summary>
/// <param name="username"></param>
/// <returns></returns>
public static string GetUserKey(string username)
{
if (UserKeys.TryGetValue(username.ToLower(), out object? value) && value is string key)
{
return key;
}
return username;
}
/// <summary>
/// 更新指定用户的密钥
/// </summary>
/// <param name="username"></param>
/// <returns></returns>
public static void UpdateUserKey(string username)
{
UserKeys.Add(username.ToLower(), Encryption.GenerateRandomString());
UserKeys.SaveConfig();
}
/// <summary>
/// 获取 API Secret Key
/// </summary>
/// <param name="token"></param>
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 "";
}
/// <summary>
/// 设置 API Secret Key
/// </summary>
/// <param name="token"></param>
/// <param name="reference1"></param>
/// <param name="reference2"></param>
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();
}
}
/// <summary>
/// 创建 SQL 服务后需要做的事
/// </summary>
@ -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
/// </summary>
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();
}

View File

@ -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, "重启服务器");
}
}

View File

@ -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<AdapterController> 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<DataRequestType>()
{
@ -53,69 +54,85 @@ namespace Milimoe.FunGame.WebAPI.Controllers
[HttpPost("login")]
public async Task<IActionResult> Login([FromBody] LoginDTO dto)
{
Config.ConnectingPlayerCount++;
try
{
PayloadModel<DataRequestType> 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<IActionResult> LogOut()
{
try
{
PayloadModel<DataRequestType> 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<RESTfulAPIModel?> 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);
}
}
}

View File

@ -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<AuthenticationSchemeOptions, CustomBearerAuthenticationHandler>("CustomBearer", options => { });
}).AddScheme<AuthenticationSchemeOptions, APIBearerAuthenticationHandler>("APIBearer", options => { }).
AddScheme<AuthenticationSchemeOptions, CustomBearerAuthenticationHandler>("CustomBearer", options => { });
builder.Logging.AddConsole(options =>
{
options.FormatterName = "CustomFormatter";

View File

@ -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<AuthenticationSchemeOptions> options, ILoggerFactory logger, UrlEncoder encoder) : AuthenticationHandler<AuthenticationSchemeOptions>(options, logger, encoder)
{
protected override async Task<AuthenticateResult> 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);
}
}
}