fix(setting):移除安全配置中的无用配置
This commit is contained in:
1 parent
df78724773
commit
e800cd4b05
5 files changed
+27
-59
No files matched your search
@@ -84,12 +84,13 @@ func (s *sAdmin) Login(ctx context.Context, req *v1.AdminLoginReq) (*v1.AdminLog
|
||||
if !crypto.VerifyPassword(req.Password, user.PasswordHash) {
|
||||
_ = common.RecordLoginHistory(ctx, "admin", user.Id, 0, "password", ipAddress, ua, deviceFP, false, "密码错误")
|
||||
|
||||
// 原子递增失败次数
|
||||
// 原子递增失败次数;锁定阈值与时长取自安全配置(sys_options)
|
||||
updateData := do.SysAdminUsers{FailedAttempts: gdb.Raw("failed_attempts + 1")}
|
||||
failedAttempts := user.FailedAttempts + 1
|
||||
maxAttempts, lockoutMinutes := common.LoginLockoutPolicy(ctx)
|
||||
|
||||
if failedAttempts >= 5 {
|
||||
lockedUntil := time.Now().Add(30 * time.Minute)
|
||||
if failedAttempts >= maxAttempts {
|
||||
lockedUntil := time.Now().Add(time.Duration(lockoutMinutes) * time.Minute)
|
||||
updateData.LockedUntil = gtime.NewFromTime(lockedUntil)
|
||||
_, err := dao.SysAdminUsers.Ctx(ctx).
|
||||
Where("id", user.Id).
|
||||
@@ -98,7 +99,7 @@ func (s *sAdmin) Login(ctx context.Context, req *v1.AdminLoginReq) (*v1.AdminLog
|
||||
g.Log().Errorf(ctx, "更新管理员账号锁定状态失败: %v", err)
|
||||
}
|
||||
return nil, common.NewBusinessError(consts.CodeAccountLocked,
|
||||
"连续 5 次密码错误,账号已锁定 30 分钟")
|
||||
fmt.Sprintf("连续 %d 次密码错误,账号已锁定 %d 分钟", maxAttempts, lockoutMinutes))
|
||||
}
|
||||
|
||||
_, err := dao.SysAdminUsers.Ctx(ctx).
|
||||
|
||||
@@ -418,6 +418,20 @@ func RegenerateBackupCodes(ctx context.Context, userType string, userID int64, c
|
||||
return plainCodes, nil
|
||||
}
|
||||
|
||||
// LoginLockoutPolicy 返回登录锁定策略(安全配置 sys_options:login_max_attempts / login_lockout_minutes)。
|
||||
// 未配置或非法值(<=0)时回退默认 5 次 / 30 分钟,管理后台与租户控制台共用同一策略。
|
||||
func LoginLockoutPolicy(ctx context.Context) (maxAttempts, lockoutMinutes int) {
|
||||
maxAttempts = Config().GetInt(ctx, "login_max_attempts")
|
||||
if maxAttempts <= 0 {
|
||||
maxAttempts = 5
|
||||
}
|
||||
lockoutMinutes = Config().GetInt(ctx, "login_lockout_minutes")
|
||||
if lockoutMinutes <= 0 {
|
||||
lockoutMinutes = 30
|
||||
}
|
||||
return maxAttempts, lockoutMinutes
|
||||
}
|
||||
|
||||
// Is2FAEnabled checks if 2FA is enabled for a user.
|
||||
func Is2FAEnabled(ctx context.Context, userType string, userID int64) (bool, error) {
|
||||
if userType == "admin" {
|
||||
|
||||
@@ -113,16 +113,11 @@ var Registry = []SettingDef{
|
||||
Label: "启用 TLS", Description: "587/465 端口建议开启"},
|
||||
|
||||
// ── Security ──
|
||||
{Key: "max_sessions_per_user", Type: SettingTypeInt, Default: "10", Category: "security",
|
||||
Label: "租户用户最大会话数", Validation: "min:1,max:100"},
|
||||
{Key: "admin_max_sessions", Type: SettingTypeInt, Default: "5", Category: "security",
|
||||
Label: "管理员最大会话数", Validation: "min:1,max:50"},
|
||||
// 会话数上限不入注册表:并发会话限制由配置文件 jwt.adminMaxSessions / jwt.tenantMaxSessions 管理
|
||||
{Key: "login_max_attempts", Type: SettingTypeInt, Default: "5", Category: "security",
|
||||
Label: "登录最大尝试次数", Validation: "min:1,max:30"},
|
||||
Label: "登录最大尝试次数", Description: "连续密码错误达到该次数后锁定账号(管理后台 + 租户控制台)", Validation: "min:1,max:30"},
|
||||
{Key: "login_lockout_minutes", Type: SettingTypeInt, Default: "30", Category: "security",
|
||||
Label: "登录锁定时长(分钟)", Validation: "min:1,max:1440"},
|
||||
{Key: "password_min_length", Type: SettingTypeInt, Default: "8", Category: "security",
|
||||
Label: "密码最小长度", Validation: "min:6,max:32"},
|
||||
Label: "登录锁定时长(分钟)", Description: "触发锁定后的持续时长(管理后台 + 租户控制台)", Validation: "min:1,max:1440"},
|
||||
{Key: "turnstile_enabled", Type: SettingTypeBool, Default: "false", Category: "security",
|
||||
Label: "启用 Turnstile 人机验证", IsPublic: true},
|
||||
{Key: "turnstile_site_key", Type: SettingTypeString, Default: "", Category: "security",
|
||||
@@ -131,8 +126,6 @@ var Registry = []SettingDef{
|
||||
Label: "Turnstile Secret Key", Sensitive: true},
|
||||
{Key: "captcha_expire_seconds", Type: SettingTypeInt, Default: "300", Category: "security",
|
||||
Label: "验证码有效期(秒)", Validation: "min:60,max:600"},
|
||||
{Key: "new_device_notification", Type: SettingTypeBool, Default: "true", Category: "security",
|
||||
Label: "新设备登录通知"},
|
||||
{Key: "register_forbidden_words", Type: SettingTypeString, Default: "admin,system,root,api,test,administrator,管理员,系统", Category: "security",
|
||||
Label: "注册禁用词", Description: "组织名称、组织代码、用户名中包含这些词时禁止注册(不区分大小写),多个禁用词用英文逗号分隔"},
|
||||
{Key: "sandbox_enabled", Type: SettingTypeBool, Default: "true", Category: "performance",
|
||||
|
||||
@@ -412,12 +412,13 @@ func (s *sTenant) Login(ctx context.Context, req *v1.TenantLoginReq) (*v1.Tenant
|
||||
if !crypto.VerifyPassword(req.Password, user.PasswordHash) {
|
||||
_ = common.RecordLoginHistory(ctx, "tenant", user.Id, tenant.Id, "password", ipAddress, ua, deviceFP, false, "密码错误")
|
||||
|
||||
// 原子递增失败次数
|
||||
// 原子递增失败次数;锁定阈值与时长取自安全配置(sys_options)
|
||||
updateData := do.TntUsers{FailedAttempts: gdb.Raw("failed_attempts + 1")}
|
||||
failedAttempts := user.FailedAttempts + 1
|
||||
maxAttempts, lockoutMinutes := common.LoginLockoutPolicy(ctx)
|
||||
|
||||
if failedAttempts >= 5 {
|
||||
lockedUntil := time.Now().Add(30 * time.Minute)
|
||||
if failedAttempts >= maxAttempts {
|
||||
lockedUntil := time.Now().Add(time.Duration(lockoutMinutes) * time.Minute)
|
||||
updateData.LockedUntil = gtime.NewFromTime(lockedUntil)
|
||||
_, err := dao.TntUsers.Ctx(ctx).
|
||||
Where("id", user.Id).
|
||||
@@ -426,7 +427,7 @@ func (s *sTenant) Login(ctx context.Context, req *v1.TenantLoginReq) (*v1.Tenant
|
||||
g.Log().Errorf(ctx, "更新账号锁定状态失败: %v", err)
|
||||
}
|
||||
return nil, common.NewBusinessError(consts.CodeAccountLocked,
|
||||
"连续 5 次密码错误,账号已锁定 30 分钟")
|
||||
fmt.Sprintf("连续 %d 次密码错误,账号已锁定 %d 分钟", maxAttempts, lockoutMinutes))
|
||||
}
|
||||
|
||||
_, err := dao.TntUsers.Ctx(ctx).
|
||||
|
||||
@@ -5,27 +5,6 @@ const values = useFormValues()
|
||||
|
||||
<template>
|
||||
<div class="tab-content">
|
||||
<!-- 会话管理 -->
|
||||
<div class="section">
|
||||
<div class="section-title">会话管理</div>
|
||||
<div class="section-grid">
|
||||
<AFormItem label="租户用户最大会话数">
|
||||
<AInputNumber
|
||||
:model-value="values['max_sessions_per_user'] as number"
|
||||
@change="(v: number | undefined) => values['max_sessions_per_user'] = v ?? 10"
|
||||
:min="1" :max="100" style="width: 100%"
|
||||
/>
|
||||
</AFormItem>
|
||||
<AFormItem label="管理员最大会话数">
|
||||
<AInputNumber
|
||||
:model-value="values['admin_max_sessions'] as number"
|
||||
@change="(v: number | undefined) => values['admin_max_sessions'] = v ?? 5"
|
||||
:min="1" :max="50" style="width: 100%"
|
||||
/>
|
||||
</AFormItem>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 登录安全 -->
|
||||
<div class="section">
|
||||
<div class="section-title">登录安全</div>
|
||||
@@ -47,20 +26,6 @@ const values = useFormValues()
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 密码策略 -->
|
||||
<div class="section">
|
||||
<div class="section-title">密码策略</div>
|
||||
<div class="section-grid">
|
||||
<AFormItem label="密码最小长度">
|
||||
<AInputNumber
|
||||
:model-value="values['password_min_length'] as number"
|
||||
@change="(v: number | undefined) => values['password_min_length'] = v ?? 8"
|
||||
:min="6" :max="32" style="width: 100%"
|
||||
/>
|
||||
</AFormItem>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 验证码有效期 -->
|
||||
<div class="section">
|
||||
<div class="section-title">滑块验证码</div>
|
||||
@@ -87,12 +52,6 @@ const values = useFormValues()
|
||||
@change="(v: string | number | boolean) => values['turnstile_enabled'] = v"
|
||||
/>
|
||||
</AFormItem>
|
||||
<AFormItem label="新设备登录通知">
|
||||
<ASwitch
|
||||
:model-value="!!values['new_device_notification']"
|
||||
@change="(v: string | number | boolean) => values['new_device_notification'] = v"
|
||||
/>
|
||||
</AFormItem>
|
||||
</div>
|
||||
<div class="section-grid">
|
||||
<AFormItem label="Site Key">
|
||||
|
||||
Reference in new issue
Block a user