diff --git a/server/admin/api_uploaduser.go b/server/admin/api_uploaduser.go index 68d48aa0..26041407 100644 --- a/server/admin/api_uploaduser.go +++ b/server/admin/api_uploaduser.go @@ -110,6 +110,7 @@ func UploadUser(file string) error { if err := dbdata.AddBatch(user); err != nil { return fmt.Errorf("请检查第%d行数据是否导入有重复用户", index) } + user.PinCode = row[4] if user.SendEmail { if err := userAccountMail(user); err != nil { return err diff --git a/server/admin/api_user.go b/server/admin/api_user.go index 19a56f90..39499643 100644 --- a/server/admin/api_user.go +++ b/server/admin/api_user.go @@ -15,6 +15,7 @@ import ( "github.com/bjdgyc/anylink/base" "github.com/bjdgyc/anylink/dbdata" + "github.com/bjdgyc/anylink/pkg/utils" "github.com/bjdgyc/anylink/sessdata" "github.com/skip2/go-qrcode" mail "github.com/xhit/go-simple-mail/v2" @@ -98,11 +99,17 @@ func UserSet(w http.ResponseWriter, r *http.Request) { return } + if len(data.PinCode) < 6 { + data.PinCode = utils.RandomRunes(8) + base.Info("用户", data.Username, "随机密码为:", data.PinCode) + } + plainpwd := data.PinCode err = dbdata.SetUser(data) if err != nil { RespError(w, RespInternalErr, err) return } + data.PinCode = plainpwd // 发送邮件 if data.SendEmail { diff --git a/server/base/cfg.go b/server/base/cfg.go index fd75af04..65cb4486 100644 --- a/server/base/cfg.go +++ b/server/base/cfg.go @@ -2,11 +2,12 @@ package base import ( "fmt" - "github.com/bjdgyc/anylink/pkg/utils" "os" "path/filepath" "reflect" "strings" + + "github.com/bjdgyc/anylink/pkg/utils" ) const ( @@ -85,9 +86,10 @@ type ServerConfig struct { Compression bool `json:"compression"` // bool NoCompressLimit int `json:"no_compress_limit"` // int - DisplayError bool `json:"display_error"` - ExcludeExportIp bool `json:"exclude_export_ip"` - AuthAloneOtp bool `json:"auth_alone_otp"` + DisplayError bool `json:"display_error"` + ExcludeExportIp bool `json:"exclude_export_ip"` + AuthAloneOtp bool `json:"auth_alone_otp"` + EncryptionPassword bool `json:"encryption_password"` AntiBruteForce bool `json:"anti_brute_force"` IPWhitelist string `json:"ip_whitelist"` diff --git a/server/base/config.go b/server/base/config.go index e6f75db4..d5ee7006 100644 --- a/server/base/config.go +++ b/server/base/config.go @@ -73,6 +73,7 @@ var configs = []config{ {Typ: cfgBool, Name: "display_error", Usage: "客户端显示详细错误信息(线上环境慎开启)", ValBool: false}, {Typ: cfgBool, Name: "exclude_export_ip", Usage: "排除出口ip路由(出口ip不加密传输)", ValBool: true}, {Typ: cfgBool, Name: "auth_alone_otp", Usage: "登录单独验证OTP窗口", ValBool: false}, + {Typ: cfgBool, Name: "encryption_password", Usage: "用户密码是否加密保存", ValBool: false}, {Typ: cfgBool, Name: "anti_brute_force", Usage: "是否开启防爆功能", ValBool: true}, {Typ: cfgStr, Name: "ip_whitelist", Usage: "全局IP白名单,多个用逗号分隔,支持单IP和CIDR范围", ValStr: "192.168.90.1,172.16.0.0/24"}, diff --git a/server/conf/server-sample.toml b/server/conf/server-sample.toml index b66477e8..87ed29fd 100644 --- a/server/conf/server-sample.toml +++ b/server/conf/server-sample.toml @@ -117,6 +117,8 @@ exclude_export_ip = true #登录单独验证OTP窗口 auth_alone_otp = false +#加密保存用户密码 +encryption_password = false #防爆破全局开关 anti_brute_force = true @@ -147,6 +149,3 @@ global_ip_lock_time = 300 #全局锁定状态的保存生命周期(秒),超过则删除记录 global_lock_state_expiration_time = 3600 - - - diff --git a/server/dbdata/user.go b/server/dbdata/user.go index 02967b93..6c84bb10 100644 --- a/server/dbdata/user.go +++ b/server/dbdata/user.go @@ -120,7 +120,7 @@ func checkLocalUser(name, pwd, group string, ext map[string]interface{}) error { } pinCode := pwd - if base.Cfg.AuthAloneOtp == false { + if !base.Cfg.AuthAloneOtp { // 判断otp信息 if !v.DisableOtp { pinCode = pwd[:pl-6] @@ -207,16 +207,18 @@ func CheckOtp(name, otp, secret string) bool { // 插入数据库前加密密码 func (u *User) BeforeInsert() { - hashedPassword, err := utils.PasswordHash(u.PinCode) - if err != nil { - base.Error(err) + if base.Cfg.EncryptionPassword { + hashedPassword, err := utils.PasswordHash(u.PinCode) + if err != nil { + base.Error(err) + } + u.PinCode = hashedPassword } - u.PinCode = hashedPassword } // 更新数据库前加密密码 func (u *User) BeforeUpdate() { - if len(u.PinCode) != 60 { + if len(u.PinCode) != 60 && base.Cfg.EncryptionPassword { hashedPassword, err := utils.PasswordHash(u.PinCode) if err != nil { base.Error(err) diff --git a/server/handler/antiBruteForce.go b/server/handler/antiBruteForce.go deleted file mode 100644 index cb6a990e..00000000 --- a/server/handler/antiBruteForce.go +++ /dev/null @@ -1,121 +0,0 @@ -package handler - -import ( - "context" - "encoding/xml" - "io" - "net" - "net/http" - "strings" - "time" - - "github.com/bjdgyc/anylink/base" -) - -// var lockManager = admin.GetLockManager() - -const loginStatusKey = "login_status" - -type HttpContext struct { - LoginStatus bool // 登录状态 -} - -// 防爆破中间件 -func antiBruteForce(next http.Handler) http.Handler { - return http.HandlerFunc(func(w http.ResponseWriter, old_r *http.Request) { - // 防爆破功能全局开关 - if !base.Cfg.AntiBruteForce { - next.ServeHTTP(w, old_r) - return - } - - // 非并发安全 - hc := &HttpContext{} - ctx := context.WithValue(context.Background(), loginStatusKey, hc) - r := old_r.WithContext(ctx) - - body, err := io.ReadAll(r.Body) - if err != nil { - http.Error(w, "Failed to read request body", http.StatusBadRequest) - return - } - defer r.Body.Close() - - cr := ClientRequest{} - err = xml.Unmarshal(body, &cr) - if err != nil { - http.Error(w, "Failed to parse XML", http.StatusBadRequest) - return - } - - username := cr.Auth.Username - if r.URL.Path == "/otp-verification" { - sessionID, err := GetCookie(r, "auth-session-id") - if err != nil { - http.Error(w, "Invalid session, please login again", http.StatusUnauthorized) - return - } - - sessionData, err := SessStore.GetAuthSession(sessionID) - if err != nil { - http.Error(w, "Invalid session, please login again", http.StatusUnauthorized) - return - } - username = sessionData.ClientRequest.Auth.Username - } - ip, _, err := net.SplitHostPort(r.RemoteAddr) // 提取纯 IP 地址,去掉端口号 - if err != nil { - http.Error(w, "Unable to parse IP address", http.StatusInternalServerError) - return - } - - now := time.Now() - // 检查IP是否在白名单中 - if lockManager.IsWhitelisted(ip) { - r.Body = io.NopCloser(strings.NewReader(string(body))) - next.ServeHTTP(w, r) - return - } - - // 检查全局 IP 锁定 - if base.Cfg.MaxGlobalIPBanCount > 0 && lockManager.CheckGlobalIPLock(ip, now) { - base.Warn("IP", ip, "is globally locked. Try again later.") - http.Error(w, "Account globally locked due to too many failed attempts. Try again later.", http.StatusTooManyRequests) - return - } - - // 检查全局用户锁定 - if base.Cfg.MaxGlobalUserBanCount > 0 && lockManager.CheckGlobalUserLock(username, now) { - base.Warn("User", username, "is globally locked. Try again later.") - http.Error(w, "Account globally locked due to too many failed attempts. Try again later.", http.StatusTooManyRequests) - return - } - - // 检查单个用户的 IP 锁定 - if base.Cfg.MaxBanCount > 0 && lockManager.CheckUserIPLock(username, ip, now) { - base.Warn("IP", ip, "is locked for user", username, "Try again later.") - http.Error(w, "Account locked due to too many failed attempts. Try again later.", http.StatusTooManyRequests) - return - } - - // 重新设置请求体以便后续处理器可以访问 - r.Body = io.NopCloser(strings.NewReader(string(body))) - - // 调用下一个处理器 - next.ServeHTTP(w, r) - - // 检查登录状态 - // Status, _ := lockManager.LoginStatus.Load(loginStatusKey) - // loginStatus, _ := Status.(bool) - - loginStatus := hc.LoginStatus - - // 更新用户登录状态 - lockManager.UpdateGlobalIPLock(ip, now, loginStatus) - lockManager.UpdateGlobalUserLock(username, now, loginStatus) - lockManager.UpdateUserIPLock(username, ip, now, loginStatus) - - // 清除登录状态 - // lockManager.LoginStatus.Delete(loginStatusKey) - }) -} diff --git a/server/handler/link_auth.go b/server/handler/link_auth.go index 2450e64b..a9c8b97f 100644 --- a/server/handler/link_auth.go +++ b/server/handler/link_auth.go @@ -102,9 +102,6 @@ func LinkAuth(w http.ResponseWriter, r *http.Request) { ext := map[string]interface{}{"mac_addr": cr.MacAddressList.MacAddress} err = dbdata.CheckUser(cr.Auth.Username, cr.Auth.Password, cr.GroupSelect, ext) if err != nil { - // lockManager.LoginStatus.Store(loginStatusKey, false) // 记录登录失败状态 - // hc := r.Context().Value(loginStatusKey).(*HttpContext) - // hc.LoginStatus = false lockManager.UpdateLoginStatus(cr.Auth.Username, r.RemoteAddr, false) // 记录登录失败状态 base.Warn(err, r.RemoteAddr) @@ -131,9 +128,6 @@ func LinkAuth(w http.ResponseWriter, r *http.Request) { } // 用户otp验证 if base.Cfg.AuthAloneOtp && !v.DisableOtp { - // lockManager.LoginStatus.Store(loginStatusKey, true) // 重置OTP验证计数 - // hc := r.Context().Value(loginStatusKey).(*HttpContext) - // hc.LoginStatus = true lockManager.UpdateLoginStatus(cr.Auth.Username, r.RemoteAddr, true) // 重置OTP验证计数 sessionID, err := GenerateSessionID() diff --git a/server/handler/link_auth_otp.go b/server/handler/link_auth_otp.go index 23558c09..aaad0378 100644 --- a/server/handler/link_auth_otp.go +++ b/server/handler/link_auth_otp.go @@ -111,9 +111,6 @@ func DeleteCookie(w http.ResponseWriter, name string) { http.SetCookie(w, cookie) } func CreateSession(w http.ResponseWriter, r *http.Request, authSession *AuthSession) { - // lockManager.LoginStatus.Store(loginStatusKey, true) // 更新登录成功状态 - // hc := r.Context().Value(loginStatusKey).(*HttpContext) - // hc.LoginStatus = true cr := authSession.ClientRequest ua := authSession.UserActLog @@ -208,14 +205,6 @@ func LinkAuth_otp(w http.ResponseWriter, r *http.Request) { // 动态码错误 if !dbdata.CheckOtp(username, otp, otpSecret) { - // if sessionData.AddOtpErrCount(1) > maxOtpErrCount { - // SessStore.DeleteAuthSession(sessionID) - // http.Error(w, "TooManyError, please login again", http.StatusBadRequest) - // return - // } - // lockManager.LoginStatus.Store(loginStatusKey, false) // 记录登录失败状态 - // hc := r.Context().Value(loginStatusKey).(*HttpContext) - // hc.LoginStatus = false lockManager.UpdateLoginStatus(username, r.RemoteAddr, false) // 记录登录失败状态 base.Warn("OTP 动态码错误", username, r.RemoteAddr)