From 871e7d47a3a38b32b951642d8bf9382fe289412a Mon Sep 17 00:00:00 2001 From: root Date: Sun, 1 Dec 2024 17:37:06 +0800 Subject: [PATCH] =?UTF-8?q?=E6=94=AF=E6=8C=81=E9=82=AE=E4=BB=B6=E7=99=BB?= =?UTF-8?q?=E5=BD=95=E6=B3=A8=E5=86=8C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/Controllers/ManagementController.cs | 1 + src/Controllers/UserController.cs | 21 ++--------- src/Models/User.cs | 34 +++++++++++++++++- src/Program.cs | 20 +++++------ src/Services/AuthService.cs | 27 +++++++------- src/Services/DatabaseGateService.cs | 48 ++++++++++++++++++++----- src/Services/LocalCacheManager.cs | 7 ++-- src/iFileProxy.json | 2 +- 8 files changed, 106 insertions(+), 54 deletions(-) diff --git a/src/Controllers/ManagementController.cs b/src/Controllers/ManagementController.cs index bb8003c..346e172 100644 --- a/src/Controllers/ManagementController.cs +++ b/src/Controllers/ManagementController.cs @@ -5,6 +5,7 @@ using Microsoft.AspNetCore.Mvc; using Serilog; using System.Text.Json; using iFileProxy.Attributes; + namespace iFileProxy.Controllers { [Authorize(UserMask.Admin,UserMask.SuperAdmin)] diff --git a/src/Controllers/UserController.cs b/src/Controllers/UserController.cs index 74217f5..75a1ea9 100644 --- a/src/Controllers/UserController.cs +++ b/src/Controllers/UserController.cs @@ -1,8 +1,8 @@ using iFileProxy.Attributes; +using iFileProxy.Helpers; using iFileProxy.Models; using iFileProxy.Services; using Microsoft.AspNetCore.Mvc; -using Newtonsoft.Json; namespace iFileProxy.Controllers { @@ -27,8 +27,8 @@ namespace iFileProxy.Controllers { try { - var ip = HttpContext.Connection.RemoteIpAddress?.ToString() ?? "unknown"; - var (success, message) = await _authService.RegisterAsync(request.Username, request.Password, ip, request.NickName); + var ip = MasterHelper.GetClientIPAddr(HttpContext); + var (success, message) = await _authService.RegisterAsync(request,ip); return Ok(new CommonRsp { @@ -106,9 +106,6 @@ namespace iFileProxy.Controllers }); } - - _logger.LogInformation(JsonConvert.SerializeObject(user)); - // 不返回敏感信息 return Ok(new CommonRsp { @@ -592,16 +589,4 @@ namespace iFileProxy.Controllers } } - public class RegisterRequest - { - public string Username { get; set; } = string.Empty; - public string Password { get; set; } = string.Empty; - public string NickName { get; set; } = string.Empty; - } - - public class LoginRequest - { - public string Username { get; set; } = string.Empty; - public string Password { get; set; } = string.Empty; - } } \ No newline at end of file diff --git a/src/Models/User.cs b/src/Models/User.cs index dbd01e5..b0dadf9 100644 --- a/src/Models/User.cs +++ b/src/Models/User.cs @@ -4,9 +4,10 @@ namespace iFileProxy.Models { public enum UserMask { + Guest = -1, // 访客 User = 0, Admin = 1, - SuperAdmin = 2 // 超级管理员 + SuperAdmin = 2, // 超级管理员 } public enum UserState { @@ -77,6 +78,12 @@ namespace iFileProxy.Models /// [JsonProperty("last_login_ip")] public string LastLoginIP { get; set; } = string.Empty; + + /// + /// 电子邮箱 + /// + [JsonProperty("email")] + public string Email { get; set; } = string.Empty; } public class UserEvent @@ -132,4 +139,29 @@ namespace iFileProxy.Models [JsonProperty("nickname")] public string Nickname { get; set; } = string.Empty; } + + + /// + /// 注册请求数据结构 + /// + public class RegisterRequest + { + public string Username { get; set; } = string.Empty; + public string Password { get; set; } = string.Empty; + public string NickName { get; set; } = string.Empty; + public string Email { get; set; } = string.Empty; + } + + /// + /// 登录请求数据结构 + /// + public class LoginRequest + { + /// + /// 用户名或邮箱 + /// + public string Username { get; set; } = string.Empty; + public string Account { get; set; } = string.Empty; + public string Password { get; set; } = string.Empty; + } } diff --git a/src/Program.cs b/src/Program.cs index 8aebddf..babdb49 100644 --- a/src/Program.cs +++ b/src/Program.cs @@ -16,18 +16,18 @@ namespace iFileProxy SerilogConfig.CreateLogger(); Serilog.ILogger logger = Log.Logger.ForContext(); - Console.Write(" "); // ǿ֢ſʼ־벻þ + Console.Write(" "); // ǿ��֢�����ſ�ʼ��������־�����벻�þ� var builder = WebApplication.CreateBuilder(args); - // CORS + // ���� CORS ���� builder.Services.AddCors(options => { options.AddPolicy("AllowFrontend", builder => { builder - .WithOrigins("http://localhost:3000", "http://admin.gitdl.cn", "https://admin.gitdl.cn","http://47.243.56.137:50050") // ǰ˵ַ + .WithOrigins("http://localhost:3000", "http://admin.gitdl.cn", "https://admin.gitdl.cn","http://47.243.56.137:50050") // ǰ�˵�ַ .AllowAnyMethod() .AllowAnyHeader() .AllowCredentials(); @@ -55,10 +55,10 @@ namespace iFileProxy builder.Services.AddSingleton(); - // ֤ + // ������֤���� builder.Services.AddScoped(); - // JWT֤ + // ����JWT��֤ builder.Services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme) .AddJwtBearer(options => { @@ -98,17 +98,17 @@ namespace iFileProxy app.UseSwaggerUI(); } - // м + // �������м�� app.UseMiddleware(); app.UseCors("AllowFrontend"); app.UseHttpsRedirection(); - // Ĭļѡ + // ����Ĭ���ļ�ѡ�� var defaultFilesOptions = new DefaultFilesOptions(); - defaultFilesOptions.DefaultFileNames.Clear(); // Ĭб - defaultFilesOptions.DefaultFileNames.Add("index.html"); // ԶĬļ + defaultFilesOptions.DefaultFileNames.Clear(); // ���Ĭ���б� + defaultFilesOptions.DefaultFileNames.Add("index.html"); // �����Զ���Ĭ���ļ� app.UseDefaultFiles(defaultFilesOptions); @@ -120,7 +120,7 @@ namespace iFileProxy app.Services.GetRequiredService>>(), AppConfig.GetCurrConfig().SecurityOptions.DailyRequestLimitPerIP); - // м + // �����м�� app.UseMiddleware(); app.MapControllers(); diff --git a/src/Services/AuthService.cs b/src/Services/AuthService.cs index 7b2a169..b07cf8f 100644 --- a/src/Services/AuthService.cs +++ b/src/Services/AuthService.cs @@ -20,14 +20,14 @@ namespace iFileProxy.Services _logger = logger; } - public async Task<(bool success, string message)> RegisterAsync(string username, string password, string ip, string nickname) + public async Task<(bool success, string message)> RegisterAsync(RegisterRequest request, string ipAddr) { try { // 检查用户名是否已存在 - if (await _dbGateService.UserExistsAsync(username)) + if (await _dbGateService.UserExistsAsync(request.Username, request.Email)) { - return (false, "用户名已存在"); + return (false, "用户名或电子邮件已存在"); } // 检查是否是第一个用户 @@ -36,12 +36,13 @@ namespace iFileProxy.Services // 创建新用户 var user = new User { - Username = username, - PasswordHash = BCrypt.Net.BCrypt.HashPassword(password), - LastLoginIP = ip, + Username = request.Username, + PasswordHash = BCrypt.Net.BCrypt.HashPassword(request.Password), + LastLoginIP = ipAddr, // 如果是第一个用户,设置为超级管理员 Mask = isFirstUser ? UserMask.SuperAdmin : UserMask.User, - Nickname = nickname + Nickname = request.NickName, + Email = request.Email, }; // 保存用户 @@ -52,12 +53,12 @@ namespace iFileProxy.Services { UserId = user.UserId, EventType = UserEventType.Registry, - EventIP = ip, - EventDetail = isFirstUser ? "管理员注册" : "用户注册" + EventIP = ipAddr, + EventDetail = isFirstUser ? "超级管理员注册" : "用户注册" }; await _dbGateService.CreateUserEventAsync(userEvent); - return (true, isFirstUser ? "管理员注册成功" : "注册成功"); + return (true, isFirstUser ? "超级管理员注册成功" : "注册成功"); } catch (Exception ex) { @@ -66,11 +67,11 @@ namespace iFileProxy.Services } } - public async Task<(bool success, string token, string message)> LoginAsync(string username, string password, string ip) + public async Task<(bool success, string token, string message)> LoginAsync(string account, string password, string ip) { try { - var user = await _dbGateService.GetUserByUsernameAsync(username); + var user = await _dbGateService.GetUserByAccountAsync(account); if (user == null) { return (false, string.Empty, "用户不存在"); @@ -97,7 +98,7 @@ namespace iFileProxy.Services UserId = user.UserId, EventType = UserEventType.Login, EventIP = ip, - EventDetail = "用户登录" + EventDetail = $"用户通过{(account.Contains('@') ? "邮箱" : "用户名")}登录" }; await _dbGateService.CreateUserEventAsync(userEvent); diff --git a/src/Services/DatabaseGateService.cs b/src/Services/DatabaseGateService.cs index 9d76e73..c9664b0 100644 --- a/src/Services/DatabaseGateService.cs +++ b/src/Services/DatabaseGateService.cs @@ -39,6 +39,7 @@ namespace iFileProxy.Services `user_id` varchar(36) NOT NULL, `username` varchar(50) NOT NULL, `nickname` varchar(50) NOT NULL, + `email` varchar(100) NOT NULL, `password_hash` varchar(255) NOT NULL, `mask` int NOT NULL DEFAULT 0, `state` int NOT NULL DEFAULT 0, @@ -46,7 +47,8 @@ namespace iFileProxy.Services `last_login_time` datetime NULL, `last_login_ip` varchar(45) NULL, PRIMARY KEY (`user_id`), - UNIQUE KEY `username` (`username`) + UNIQUE KEY `username` (`username`), + UNIQUE KEY `email` (`email`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; """; @@ -196,6 +198,20 @@ namespace iFileProxy.Services return n; } + /// + /// 内部查询数据专用 当此方法暴露给C端可能造成sql注入等安全问题 + /// + /// SQL语句 + /// 配置文件中的Description字段 + /// 影响的行数 + public int Query(string sql, MySqlConnection connection) + { + using MySqlCommand sqlCmd = new(sql, connection); + int n = sqlCmd.ExecuteNonQuery(); + _logger.Debug($"查询完成, 受影响的行数: {n}"); + return n; + } + public List CheckCacheDependencies(string taskId,string ipAddr) { string sql = $"SELECT * FROM t_tasks_info WHERE `status` = @status AND `tag` = @tag AND `client_ip` <> @ip_addr"; @@ -362,10 +378,11 @@ namespace iFileProxy.Services } - public bool UpdateTaskStatus(TaskInfo taskInfo) + public bool UpdateTaskStatus(TaskInfo taskInfo, MySqlConnection? connection = null) { string sql = @"UPDATE t_tasks_info set `status` = @status , update_time = Now() WHERE `tid` = @tid"; - MySqlConnection conn = GetAndOpenDBConn("iFileProxy_Db"); + MySqlConnection conn = connection ?? GetAndOpenDBConn("iFileProxy_Db"); + try { using MySqlCommand sqlCmd = new (sql, conn); @@ -623,12 +640,13 @@ namespace iFileProxy.Services return users.FirstOrDefault(); } - public async Task UserExistsAsync(string username) + public async Task UserExistsAsync(string username, string email) { - var sql = "SELECT COUNT(*) FROM t_users WHERE username = @username"; + var sql = "SELECT COUNT(*) FROM t_users WHERE username = @username OR email = @email"; var parameters = new Dictionary { - { "@username", username } + { "@username", username }, + { "@email", email } }; var count = await ExecuteScalarAsync(sql, parameters); return count > 0; @@ -636,8 +654,8 @@ namespace iFileProxy.Services public async Task CreateUserAsync(User user) { - var sql = @"INSERT INTO t_users (user_id, username, password_hash, mask, state, create_time, last_login_time, last_login_ip, nickname) - VALUES (@userId, @username, @passwordHash, @mask, @state, @createTime, @lastLoginTime, @lastLoginIp, @nickname)"; + var sql = @"INSERT INTO t_users (user_id, username, password_hash, mask, state, create_time, last_login_time, last_login_ip, nickname, email) + VALUES (@userId, @username, @passwordHash, @mask, @state, @createTime, @lastLoginTime, @lastLoginIp, @nickname, @email)"; var parameters = new Dictionary { @@ -650,7 +668,7 @@ namespace iFileProxy.Services { "@lastLoginTime", user.LastLoginTime }, { "@lastLoginIp", user.LastLoginIP }, { "@nickname", user.Nickname }, - + { "@email", user.Email } }; var result = await ExecuteNonQueryAsync(sql, parameters); @@ -870,5 +888,17 @@ namespace iFileProxy.Services Data = events }; } + + public async Task GetUserByAccountAsync(string account) + { + var sql = "SELECT * FROM t_users WHERE username = @account OR email = @account"; + var parameters = new Dictionary + { + { "@account", account } + }; + + var users = await ExecuteQueryAsync(sql, parameters); + return users.FirstOrDefault(); + } } } diff --git a/src/Services/LocalCacheManager.cs b/src/Services/LocalCacheManager.cs index a003150..aa44e43 100644 --- a/src/Services/LocalCacheManager.cs +++ b/src/Services/LocalCacheManager.cs @@ -38,6 +38,9 @@ namespace iFileProxy.Services /// public void CheckAndCleanCache(object state) { + // 初始化并打开一个MySQL连接 防止后续数据过多导致程序crush + var dbConn = _dbGateService.GetAndOpenDBConn(DbConfigName.iFileProxy); + // 获取数据库中超出生命周期的缓存数据 string result = _dbGateService.QueryTableData($"SELECT * FROM t_tasks_info WHERE UNIX_TIMESTAMP(NOW()) - UNIX_TIMESTAMP(update_time) > {CACHE_LIFETIME} AND (tag <> 'CLEANED' OR tag IS NULL)", DbConfigName.iFileProxy); List? taskInfos = JsonConvert.DeserializeObject>(result); @@ -59,9 +62,9 @@ namespace iFileProxy.Services throw; } } - _dbGateService.Query($"UPDATE t_tasks_info SET `tag` = \"CLEANED\" WHERE `tid` = '{taskInfo.TaskId}'", DbConfigName.iFileProxy); + _dbGateService.Query($"UPDATE t_tasks_info SET `tag` = \"CLEANED\" WHERE `tid` = '{taskInfo.TaskId}'", dbConn); taskInfo.Status = TaskState.Cleaned; - _dbGateService.UpdateTaskStatus(taskInfo); + _dbGateService.UpdateTaskStatus(taskInfo,dbConn); } } } diff --git a/src/iFileProxy.json b/src/iFileProxy.json index 27e45a4..bf3f1b0 100644 --- a/src/iFileProxy.json +++ b/src/iFileProxy.json @@ -16,7 +16,7 @@ "Download": { "SavePath": "./download/", // 临时文件保存位置 "ThreadNum": 4, // 下载线程数 - "MaxAllowedFileSize": 65536, // 允许代理的最大文件尺寸 + "MaxAllowedFileSize": 1000000000, // 允许代理的最大文件尺寸 "MaxParallelTasks": 4, // 同一时间最大并行任务数 "MaxQueueLength": 60, // 最大等待队列长度 "Aria2cPath": "./lib/aria2c",