From b855400cec5acdbb5265bdce487ea830e86e6483 Mon Sep 17 00:00:00 2001 From: engigu Date: Tue, 10 Feb 2026 14:19:32 +0800 Subject: [PATCH] feat: adjust deploy password and login limit --- CHANGELOG.md | 2 +- README.md | 5 ++-- internal/controllers/auth_controller.go | 32 +++++++++++++++++++++++++ internal/services/init_service.go | 11 +++++++-- internal/utils/random.go | 18 ++++++++++++++ internal/utils/response.go | 4 ++++ 6 files changed, 67 insertions(+), 5 deletions(-) create mode 100644 internal/utils/random.go diff --git a/CHANGELOG.md b/CHANGELOG.md index 37b36f5..02bf072 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,6 @@ 启动后访问:http://localhost:8052 -默认账号:`admin` / `123456` +默认账号:用户名 `admin`,密码见启动日志(首次启动随机生成) diff --git a/README.md b/README.md index 91881c0..7dd8d98 100644 --- a/README.md +++ b/README.md @@ -13,6 +13,7 @@ ### 最近更新 +**2026.02.11** - 增强安全性:首次启动使用随机密码并打印在日志中,登录接口增加防暴力破解,文件系统操作增加路径穿越锁定 **2026.02.10** - 重构任务调度系统,完善并发控制,优化文件树交互体验,支持任务执行实时日志流 **2026.02.06** - 整理 Docker 目录结构,增加 Alpine 及 Debian 13 (Trixie) 镜像支持 @@ -501,9 +502,9 @@ nginx -t && nginx -s reload 启动后访问:http://localhost:8052 -**默认账号:** `admin` / `123456` +**默认账号:** 用户名 `admin`,密码见启动日志(首次启动会自动生成 12 位随机密码并打印在日志中) -> 首次登录后请立即修改默认密码 +> **注意**:出于安全性考虑,系统不再使用固定默认密码。请在容器启动日志中搜索 `管理员账号创建成功` 找到您的随机密码,并登录后及时修改。 ### 数据目录 diff --git a/internal/controllers/auth_controller.go b/internal/controllers/auth_controller.go index d8e1e67..5b20c5a 100644 --- a/internal/controllers/auth_controller.go +++ b/internal/controllers/auth_controller.go @@ -2,6 +2,8 @@ package controllers import ( "strconv" + "sync" + "time" "github.com/engigu/baihu-panel/internal/constant" "github.com/engigu/baihu-panel/internal/middleware" @@ -17,6 +19,13 @@ type AuthController struct { loginLogService *services.LoginLogService } +type loginAttempt struct { + Count int + LastAttempt time.Time +} + +var loginAttempts sync.Map + func NewAuthController(userService *services.UserService, settingsService *services.SettingsService, loginLogService *services.LoginLogService) *AuthController { return &AuthController{ userService: userService, @@ -39,14 +48,37 @@ func (ac *AuthController) Login(c *gin.Context) { return } + // 暴力破解防御 + if val, ok := loginAttempts.Load(ip); ok { + attempt := val.(*loginAttempt) + if attempt.Count >= 5 && time.Since(attempt.LastAttempt) < time.Minute { + ac.loginLogService.Create(req.Username, ip, userAgent, "failed", "尝试次数过多,请一分钟后再试") + utils.TooManyRequests(c, "尝试次数过多,请一分钟后再试") + return + } + // 如果距离上次尝试已超过一分钟,重置计数 + if time.Since(attempt.LastAttempt) >= time.Minute { + loginAttempts.Delete(ip) + } + } + user := ac.userService.GetUserByUsername(req.Username) if user == nil || !ac.userService.ValidatePassword(user, req.Password) { + // 记录失败尝试 + val, _ := loginAttempts.LoadOrStore(ip, &loginAttempt{Count: 0, LastAttempt: time.Now()}) + attempt := val.(*loginAttempt) + attempt.Count++ + attempt.LastAttempt = time.Now() + // 记录登录失败日志 ac.loginLogService.Create(req.Username, ip, userAgent, "failed", "用户名或密码错误") utils.Unauthorized(c, "用户名或密码错误") return } + // 登录成功,清除尝试记录 + loginAttempts.Delete(ip) + // 获取 cookie 过期天数 expireDays := 7 if days := ac.settingsService.Get(constant.SectionSite, constant.KeyCookieDays); days != "" { diff --git a/internal/services/init_service.go b/internal/services/init_service.go index a7d0b3d..ebb6e3a 100644 --- a/internal/services/init_service.go +++ b/internal/services/init_service.go @@ -2,6 +2,7 @@ package services import ( "github.com/engigu/baihu-panel/internal/logger" + "github.com/engigu/baihu-panel/internal/utils" ) type InitService struct { @@ -40,6 +41,12 @@ func (s *InitService) initializeAdmin(userService *UserService) { return } - userService.CreateUser("admin", "123456", "admin@local", "admin") - logger.Info("管理员账号创建成功: admin / 123456") + password := utils.RandomString(12) + userService.CreateUser("admin", password, "admin@local", "admin") + logger.Infof("--------------------------------------------------") + logger.Infof("管理员账号创建成功:") + logger.Infof("用户名: admin") + logger.Infof("密 码: %s", password) + logger.Infof("请妥善保管您的密码,并登录后及时修改。") + logger.Infof("--------------------------------------------------") } diff --git a/internal/utils/random.go b/internal/utils/random.go new file mode 100644 index 0000000..bf99e58 --- /dev/null +++ b/internal/utils/random.go @@ -0,0 +1,18 @@ +package utils + +import ( + "crypto/rand" + "math/big" +) + +const charset = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789" + +// RandomString 生成指定长度的随机字符串 +func RandomString(n int) string { + b := make([]byte, n) + for i := range b { + num, _ := rand.Int(rand.Reader, big.NewInt(int64(len(charset)))) + b[i] = charset[num.Int64()] + } + return string(b) +} diff --git a/internal/utils/response.go b/internal/utils/response.go index ae9e773..3e3d41b 100644 --- a/internal/utils/response.go +++ b/internal/utils/response.go @@ -50,6 +50,10 @@ func NotFound(c *gin.Context, msg string) { Error(c, 404, msg) } +func TooManyRequests(c *gin.Context, msg string) { + Error(c, 429, msg) +} + func ServerError(c *gin.Context, msg string) { Error(c, 500, msg) }