支持邮件登录注册
This commit is contained in:
parent
9f300df47b
commit
871e7d47a3
8 changed files with 106 additions and 54 deletions
|
@ -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)]
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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
|
|||
/// </summary>
|
||||
[JsonProperty("last_login_ip")]
|
||||
public string LastLoginIP { get; set; } = string.Empty;
|
||||
|
||||
/// <summary>
|
||||
/// 电子邮箱
|
||||
/// </summary>
|
||||
[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;
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// 注册请求数据结构
|
||||
/// </summary>
|
||||
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;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 登录请求数据结构
|
||||
/// </summary>
|
||||
public class LoginRequest
|
||||
{
|
||||
/// <summary>
|
||||
/// 用户名或邮箱
|
||||
/// </summary>
|
||||
public string Username { get; set; } = string.Empty;
|
||||
public string Account { get; set; } = string.Empty;
|
||||
public string Password { get; set; } = string.Empty;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -16,18 +16,18 @@ namespace iFileProxy
|
|||
SerilogConfig.CreateLogger();
|
||||
Serilog.ILogger logger = Log.Logger.ForContext<Program>();
|
||||
|
||||
Console.Write(" "); // 强迫症,看着开始的那条日志不对齐不得劲
|
||||
Console.Write(" "); // ǿ<EFBFBD><EFBFBD>֢<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ſ<EFBFBD>ʼ<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>־<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>벻<EFBFBD>þ<EFBFBD>
|
||||
|
||||
var builder = WebApplication.CreateBuilder(args);
|
||||
|
||||
// 添加 CORS 服务
|
||||
// <EFBFBD><EFBFBD><EFBFBD><EFBFBD> CORS <20><><EFBFBD><EFBFBD>
|
||||
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") // ǰ<EFBFBD>˵<EFBFBD>ַ
|
||||
.AllowAnyMethod()
|
||||
.AllowAnyHeader()
|
||||
.AllowCredentials();
|
||||
|
@ -55,10 +55,10 @@ namespace iFileProxy
|
|||
|
||||
builder.Services.AddSingleton<TaskManager>();
|
||||
|
||||
// 添加认证服务
|
||||
// <EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>֤<EFBFBD><EFBFBD><EFBFBD><EFBFBD>
|
||||
builder.Services.AddScoped<AuthService>();
|
||||
|
||||
// 添加JWT认证
|
||||
// <EFBFBD><EFBFBD><EFBFBD><EFBFBD>JWT<EFBFBD><EFBFBD>֤
|
||||
builder.Services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
|
||||
.AddJwtBearer(options =>
|
||||
{
|
||||
|
@ -98,17 +98,17 @@ namespace iFileProxy
|
|||
app.UseSwaggerUI();
|
||||
}
|
||||
|
||||
// 错误处理中间件
|
||||
// <EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>м<EFBFBD><EFBFBD>
|
||||
app.UseMiddleware<ErrorHandlerMiddleware>();
|
||||
|
||||
app.UseCors("AllowFrontend");
|
||||
|
||||
app.UseHttpsRedirection();
|
||||
|
||||
// 配置默认文件选项
|
||||
// <EFBFBD><EFBFBD><EFBFBD><EFBFBD>Ĭ<EFBFBD><EFBFBD><EFBFBD>ļ<EFBFBD>ѡ<EFBFBD><EFBFBD>
|
||||
var defaultFilesOptions = new DefaultFilesOptions();
|
||||
defaultFilesOptions.DefaultFileNames.Clear(); // 清除默认列表
|
||||
defaultFilesOptions.DefaultFileNames.Add("index.html"); // 添加自定义默认文件
|
||||
defaultFilesOptions.DefaultFileNames.Clear(); // <EFBFBD><EFBFBD><EFBFBD>Ĭ<EFBFBD><EFBFBD><EFBFBD>б<EFBFBD>
|
||||
defaultFilesOptions.DefaultFileNames.Add("index.html"); // <EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>Զ<EFBFBD><EFBFBD><EFBFBD>Ĭ<EFBFBD><EFBFBD><EFBFBD>ļ<EFBFBD>
|
||||
|
||||
app.UseDefaultFiles(defaultFilesOptions);
|
||||
|
||||
|
@ -120,7 +120,7 @@ namespace iFileProxy
|
|||
app.Services.GetRequiredService<Dictionary<string, Dictionary<string, uint>>>(),
|
||||
AppConfig.GetCurrConfig().SecurityOptions.DailyRequestLimitPerIP);
|
||||
|
||||
// 添加中间件
|
||||
// <EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>м<EFBFBD><EFBFBD>
|
||||
app.UseMiddleware<JwtMiddleware>();
|
||||
|
||||
app.MapControllers();
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 内部查询数据专用 当此方法暴露给C端可能造成sql注入等安全问题
|
||||
/// </summary>
|
||||
/// <param name="sql">SQL语句</param>
|
||||
/// <param name="dbConfName">配置文件中的Description字段</param>
|
||||
/// <returns>影响的行数</returns>
|
||||
public int Query(string sql, MySqlConnection connection)
|
||||
{
|
||||
using MySqlCommand sqlCmd = new(sql, connection);
|
||||
int n = sqlCmd.ExecuteNonQuery();
|
||||
_logger.Debug($"查询完成, 受影响的行数: {n}");
|
||||
return n;
|
||||
}
|
||||
|
||||
public List<TaskInfo> 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<bool> UserExistsAsync(string username)
|
||||
public async Task<bool> 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<string, object>
|
||||
{
|
||||
{ "@username", username }
|
||||
{ "@username", username },
|
||||
{ "@email", email }
|
||||
};
|
||||
var count = await ExecuteScalarAsync<long>(sql, parameters);
|
||||
return count > 0;
|
||||
|
@ -636,8 +654,8 @@ namespace iFileProxy.Services
|
|||
|
||||
public async Task<bool> 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<string, object>
|
||||
{
|
||||
|
@ -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<User?> GetUserByAccountAsync(string account)
|
||||
{
|
||||
var sql = "SELECT * FROM t_users WHERE username = @account OR email = @account";
|
||||
var parameters = new Dictionary<string, object>
|
||||
{
|
||||
{ "@account", account }
|
||||
};
|
||||
|
||||
var users = await ExecuteQueryAsync<User>(sql, parameters);
|
||||
return users.FirstOrDefault();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -38,6 +38,9 @@ namespace iFileProxy.Services
|
|||
/// </summary>
|
||||
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<TaskInfo>? taskInfos = JsonConvert.DeserializeObject<List<TaskInfo>>(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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -16,7 +16,7 @@
|
|||
"Download": {
|
||||
"SavePath": "./download/", // 临时文件保存位置
|
||||
"ThreadNum": 4, // 下载线程数
|
||||
"MaxAllowedFileSize": 65536, // 允许代理的最大文件尺寸
|
||||
"MaxAllowedFileSize": 1000000000, // 允许代理的最大文件尺寸
|
||||
"MaxParallelTasks": 4, // 同一时间最大并行任务数
|
||||
"MaxQueueLength": 60, // 最大等待队列长度
|
||||
"Aria2cPath": "./lib/aria2c",
|
||||
|
|
Loading…
Reference in a new issue