DevOps平台 — 第四篇:密码策略与安全防护

密码是系统安全的第一道防线,也是最容易被打穿的一道防线。用户习惯用弱密码、重复密码、键盘连续密码------如果平台不做强制约束,一次撞库攻击就能让整个系统沦陷。本篇记录 DevOps 平台的密码策略模块设计:从策略配置到弱口令管理,从扩展规则到登录防护,如何用一套「端口-适配器」架构把安全能力做成可插拔的基础组件。

一、整体架构:三层分离

密码策略不是一个独立功能,而是一组贯穿「注册 → 登录 → 改密 → 登录防护」全链路的安全能力。平台将它拆成三层:

markdown 复制代码
modules-core(接口层)
  ├─ PasswordPolicySnapshot        策略快照(纯 POJO,无持久化注解)
  ├─ PasswordPolicyProvider        策略提供接口(business 实现)
  ├─ PasswordSecurityService       密码校验 + 登录防护接口
  ├─ PasswordCredentialService     写密门面接口(校验 + 哈希 + 强制改密标记)
  ├─ LoginSecurityFacade           登录安全编排接口
  └─ PasswordAccountPort           账号端口接口(business 实现)
       │
modules-security(实现层)
  ├─ DefaultPasswordSecurityService   策略校验 + Redis 登录失败防护
  ├─ DefaultPasswordCredentialService 写密场景统一处理
  ├─ DefaultLoginSecurityFacade       登录安全编排
  ├─ RedisLoginLockStore              登录失败计数 / 临时锁
  └─ ForcePwdChangeInterceptor        强制改密拦截器
       │
business-system(数据层)
  ├─ SysPasswordPolicyProvider       读库组装策略快照 + 本地缓存
  ├─ SysPasswordPolicyController     策略配置 CRUD
  ├─ SysPasswordWeakWordController   弱口令管理 CRUD
  └─ SysPasswordPolicyRuleController 扩展规则管理 CRUD

为什么这样分? modules-core 只定义接口和快照对象,不依赖任何数据库、Redis 或业务模块。modules-security 提供默认实现,依赖 Redis 做登录防护。business-system 负责从数据库读取策略、弱口令、扩展规则,组装成快照供安全模块消费。

如果将来要换掉 Redis(比如改用 Caffeine 做本地缓存),只需要在 modules-security 中替换 RedisLoginLockStore,不影响策略配置和业务逻辑。这就是端口-适配器模式的价值。

二、策略配置

2.1 单记录设计

密码策略是全局唯一的------整个平台只有一份当前生效的策略,不是每个企业一套。sys_password_policy 表通过 LIMIT 1 查询当前策略记录,更新时只允许修改已有记录字段,不允许新增第二条。

这种设计避免了「多条策略到底哪条生效」的歧义,也简化了管理界面------不需要选择策略、不需要设置优先级,配置即生效。

2.2 策略字段总览

策略配置分为五个维度,共 20+ 个可调参数:

基础强度

字段 默认值 说明
enabled true 策略总开关
minLength 8 最短密码长度
maxLength 64 最长密码长度
requireUpper true 必须包含大写字母
requireLower true 必须包含小写字母
requireDigit true 必须包含数字
requireSpecial false 必须包含特殊字符

内容约束

字段 默认值 说明
forbidUsername true 禁止密码包含用户名
maxRepeatChar 3 同一字符连续重复上限
forbidKeyboardSeq true 禁止键盘连续序列(qwer、1234 等)
forbidCommonWeak true 禁止弱口令词库中的密码

生命周期

字段 默认值 说明
historyCount 3 历史密码不可重复次数
minAgeDays 0 最短改密间隔天数(防止频繁改密)
expireDays 90 密码有效天数
expireWarnDays 7 到期前提醒天数
tempPwdExpireHours 24 临时密码有效小时数

强制改密

字段 默认值 说明
forceChangeFirst true 首次登录强制改密
forceChangeReset true 管理员重置后强制改密
forceChangeOnPolicy false 策略升级后强制改密
pwdChangeKickSession true 改密后踢掉其他会话

登录防护

字段 默认值 说明
maxFailCount 5 最大登录失败次数
failWindowMinutes 15 失败统计窗口
failLockMinutes 30 临时锁定分钟数(0=永久锁定)
captchaAfterFails 3 触发验证码的失败次数

功能截图:

2.3 策略快照与缓存

策略配置修改后不需要重启服务即可生效。核心机制是 SysPasswordPolicyProvider

scss 复制代码
策略读取流程
  ├─ getCurrent()
  │    ├─ AtomicReference 缓存命中 → 直接返回快照
  │    └─ 缓存为空 → loadFromDb()
  │         ├─ 查 sys_password_policy(LIMIT 1)
  │         ├─ 查 sys_password_weak_word(启用的词列表)
  │         └─ 查 sys_password_policy_rule(启用的规则 Map)
  │              → 组装 PasswordPolicySnapshot
  │              → 写入缓存
  │
  └─ evictCache()
       └─ 策略/弱口令/规则 CRUD 后主动清除缓存
            → 下次读取时重新加载

为什么用 AtomicReference 而不是 @Cacheable 策略快照是一个聚合对象------需要同时读取策略主表、弱口令词库、扩展规则三张表才能组装完成。@Cacheable 适合单方法单 key 的缓存,而这里需要三表联合加载后缓存为一个整体。AtomicReference 配合 evictCache() 主动失效,简单直接。

每次策略修改、弱口令增删改、扩展规则启停,都会调用 evictCache() 清除缓存,下次请求时重新从数据库加载。这保证了配置的实时生效,同时避免了每次校验密码都查三张表的性能开销。

2.4 密码校验链

当用户设置或修改密码时,密码会经过一条完整的校验链:

复制代码
密码校验链(DefaultPasswordSecurityService.validate)
  ├─ 策略未启用或为空 → 直接通过
  ├─ 密码为空 → 失败
  ├─ 长度校验(minLength / maxLength)
  ├─ 字符类型校验(大写 / 小写 / 数字 / 特殊)
  ├─ 禁止包含用户名(forbidUsername)
  ├─ 连续重复字符校验(maxRepeatChar)
  ├─ 弱口令精确匹配(forbidCommonWeak → weakWords 列表)
  ├─ 键盘序列校验(forbidKeyboardSeq → 读取扩展规则或默认序列)
  └─ 历史密码校验(historyCount → BCrypt 比对历史哈希)

弱口令匹配策略: 不是模糊包含,而是精确匹配 (全小写对比)。password123 不会被 password 拦截,只有密码本身是 password 时才命中。这避免了误伤------如果用户密码是 Password@2024,虽然包含 password 字段,但整体强度足够,不应被拦截。

键盘序列检测: 默认检测数字序列 0123456789、字母序列 abcdefghijklmnopqrstuvwxyz 以及 QWERTY 键盘的三行 qwertyuiopasdfghjklzxcvbnm。同时检测正向和反向序列(如 4321trewq)。最小序列长度默认 4,可通过扩展规则自定义。

历史密码校验: 用户改密时传入当前密码哈希列表(BCrypt),按 historyCount 取最近 N 条逐一比对。如果新密码与任何一条历史密码匹配,拒绝修改。BCrypt 比对是安全的------不存储明文,只比对哈希。

三、弱口令管理

3.1 弱口令词库

弱口令管理是一个独立的 CRUD 功能,维护一份「不允许用作密码」的词库。词库中的词条在策略开启 forbidCommonWeak 时生效,用户设置的密码如果与词库中某个词条精确匹配(全小写),则被拒绝。

弱口令实体 SysPasswordWeakWord 包含:

  • 弱口令:词条内容,统一小写存储
  • 类型builtin(内置)或 custom(自定义)
  • 备注:词条说明

3.2 内置与自定义

词条分为两类:

  • 内置词条 :系统初始化时预置的常见弱口令(如 passwordadmin123qwerty 等)。内置词条不允许修改原文、不允许删除,只能禁用。
  • 自定义词条:管理员手动添加或批量导入的词条。可以修改、删除。

这种设计保证了基础弱口令防护不会被误删除------管理员可以禁用某个内置词条(如果业务上有特殊需求),但不能删除它。自定义词条则完全由管理员维护。

3.3 批量导入

弱口令管理支持批量添加,支持换行分隔、逗号分隔、空格分隔。后端会自动规范化(trim + 小写)并去重,已存在的词条自动跳过。批量导入完成后返回成功数、跳过数、总解析数。

scss 复制代码
批量导入流程
  ├─ 接收原始文本(换行 / 逗号 / 空格分隔)
  ├─ 逐行拆分 + trim + 小写规范化
  ├─ LinkedHashSet 去重(保序)
  ├─ 逐条检查是否已存在
  │    ├─ 已存在 → skipped++
  │    └─ 不存在 → 插入数据库 → success++
  ├─ 若 success > 0 → evictCache() 清除策略缓存
  └─ 返回 BatchResult(success / skipped / total)

功能截图:

3.4 缓存联动

弱口令的任何增删改操作都会触发 SysPasswordPolicyProvider.evictCache(),清除策略快照缓存。下次密码校验时重新加载弱口令列表。这保证了新增弱口令后立即生效,不需要重启服务。

四、扩展规则

4.1 为什么需要扩展规则

策略主表覆盖的是通用规则(长度、字符类型、弱口令等),但有些规则需要更灵活的配置------比如键盘序列的自定义模式、渐进式锁定策略、相似度检测等。这些规则的配置格式各不相同,不适合用固定字段表示。

扩展规则设计为 ruleCode → ruleValue(JSON) 的键值对结构,ruleValue 是 JSON 字符串,由安全模块按 ruleCode 解析。这样既保持了策略主表的简洁,又提供了无限的扩展能力。

4.2 扩展规则实体

扩展规则实体 SysPasswordPolicyRule 包含:

  • 规则编码 :全局唯一,一旦创建不允许修改(与策略编码、企业编码等设计一致)
  • 规则名称:展示用
  • 启用状态:可启停
  • 优先级:控制规则排序
  • 规则配置:JSON 格式,按规则编码有不同的字段结构
  • 备注:规则说明

规则编码不可修改------编码是安全模块解析规则的 key,修改编码会导致规则与实现脱节。

4.3 已实现的扩展规则

当前已实现三种扩展规则:

keyboard_seq --- 键盘序列检测

json 复制代码
{
  "minLen": 4,
  "patterns": ["qwertyuiop", "asdfghjkl", "zxcvbnm", "0123456789"]
}
  • minLen:最小序列长度,低于此长度的子串不检测
  • patterns:自定义键盘序列模式列表

如果扩展规则未配置或解析失败,回退到默认的五组序列(数字、字母、QWERTY 三行)。

progressive_lock --- 渐进式锁定

json 复制代码
{
  "steps": [
    { "fails": 3, "lockMinutes": 5 },
    { "fails": 5, "lockMinutes": 15 },
    { "fails": 8, "lockMinutes": 60 }
  ]
}

不同于策略主表中的 maxFailCount + failLockMinutes(一次性锁定),渐进式锁定支持多档阶梯------失败 3 次锁 5 分钟,失败 5 次锁 15 分钟,失败 8 次锁 1 小时。越往后锁定时间越长,有效抑制暴力破解。

similar_check --- 密码相似度检测

json 复制代码
{
  "enabled": true,
  "maxSimilarity": 0.7
}

检测新密码与旧密码的相似度,超过阈值则拒绝修改。防止用户把 Password@1 改成 Password@2 来绕过历史密码校验。

功能截图:

4.4 规则解析的安全兜底

扩展规则的 ruleValue 是 JSON 字符串,解析时可能出现格式错误。安全模块的处理策略是:解析失败时回退默认值 。比如 keyboard_seq 规则 JSON 解析失败,自动使用默认的五组键盘序列。这保证了规则配置错误不会导致密码校验功能整体不可用。

javascript 复制代码
扩展规则解析流程(以 keyboard_seq 为例)
  ├─ 从策略快照读取 rules.get("keyboard_seq")
  ├─ ruleValue 为空?
  │    └─ 是 → 使用默认五组键盘序列
  ├─ JSON.parse(ruleValue)
  │    ├─ 解析成功
  │    │    ├─ 提取 minLen(默认 4,最小 2)
  │    │    └─ 提取 patterns 数组(替换默认序列)
  │    └─ 解析失败(JSON 格式错误)
  │         └─ 静默忽略 → 回退默认五组序列
  └─ 逐 pattern 滑动窗口检测(正向 + 反向)
       └─ 密码包含序列子串 → 拦截

五、登录防护

5.1 登录时密码验证全流程

用户从输入密码到最终登录成功,密码要经历「身份验证 → 强度策略校验 → 登录后评估」三个阶段。下面是一张完整流程图,串联了密码校验链、BCrypt 比对、登录后策略评估和强制改密拦截:

scss 复制代码
登录时密码验证全流程
  │
  ├─ ① 身份验证阶段
  │    ├─ 用户提交 username + password
  │    ├─ assertNotTempLocked(username)
  │    │    ├─ Redis lock key 存在 → 拒绝「账号已锁定,N 秒后重试」
  │    │    └─ 未锁定 → 继续
  │    ├─ needCaptcha(username)?
  │    │    ├─ 是 → 校验验证码 → 失败则拒绝
  │    │    └─ 否 → 继续
  │    ├─ PasswordAccountPort.findByUsername(username)
  │    │    ├─ 用户不存在 → onLoginFail(username, null) → 返回错误
  │    │    └─ 用户存在 → 取出 passwordHash
  │    ├─ BCrypt.checkpw(plainPassword, passwordHash)
  │    │    ├─ 不匹配 → onLoginFail(username, userId) → 累计失败/锁定 → 返回错误
  │    │    └─ 匹配 → 进入 ②
  │
  ├─ ② 密码强度策略校验阶段(登录后评估)
  │    ├─ onLoginSuccess(username) → 清除 Redis fail/lock key
  │    ├─ evaluatePostLoginPolicy(userId, username, plainPassword)
  │    │    ├─ 读取当前策略快照(PasswordPolicySnapshot)
  │    │    ├─ 用当前策略重新校验本次登录密码
  │    │    │    ├─ 长度 / 字符类型 / 用户名 / 重复字符
  │    │    │    ├─ 弱口令精确匹配
  │    │    │    ├─ 键盘序列检测
  │    │    │    └─ (登录时不校验历史密码,只校验当前密码是否符合策略)
  │    │    ├─ 校验结果
  │    │    │    ├─ 通过 + 无强制标记 → 正常签发 Token
  │    │    │    ├─ 通过 + 有强制标记 → 签发 Token + 前端提示改密
  │    │    │    └─ 不通过 → setForcePwdChange(userId, true) → 签发 Token + 前端强制改密
  │    │    └─ 返回 PostLoginPolicyResult(needChangePwd + changePwdReason)
  │    └─ 签发 Sa-Token JWT
  │
  └─ ③ 后续请求拦截阶段
       ├─ ForcePwdChangeInterceptor.preHandle()
       │    ├─ 未登录 → 放行
       │    ├─ 已登录 + 无强制标记 → 放行
       │    └─ 已登录 + 有强制标记
       │         ├─ 请求在白名单(login / change-pwd / logout / info)→ 放行
       │         └─ 请求不在白名单 → 抛 NEED_CHANGE_PWD → 前端跳转改密页
       └─ 用户改密成功 → setForcePwdChange(userId, false) → 恢复正常访问

三个阶段的关系:

  • ① 身份验证:回答「这个密码对不对」------BCrypt 比对数据库中的哈希。
  • ② 策略校验:回答「这个密码够不够安全」------即使用户知道密码(BCrypt 匹配),但如果密码不符合当前策略(比如策略升级了 minLength),登录后立即标记强制改密。
  • ③ 拦截防护:回答「不安全的密码能不能用系统」------被标记强制改密的用户,除改密/登出外的一切请求都被拦截器拦住。

这种设计确保了一个关键安全属性:用户能用旧密码登录(不断服务),但登录后必须立即改密码。如果直接拒绝登录,用户会被困在外面无法自助改密;如果放行不管,弱密码就一直存在。登录后评估 + 拦截器是两者的平衡。

5.2 登录失败计数

登录失败防护由 RedisLoginLockStore 实现,使用 Redis 的两个 key 模式:

vbnet 复制代码
security:pwd:fail:{username}   → 失败计数(INCR,窗口内有效)
security:pwd:lock:{username}   → 临时锁定标记(SET + TTL)

登录防护的完整链路从用户提交登录请求开始,一直到最终返回结果:

scss 复制代码
登录安全全流程
  ├─ 用户提交登录请求
  ├─ assertNotTempLocked(username)
  │    ├─ Redis lock key 存在?
  │    │    ├─ 是 → 返回「账号已锁定,N 秒后重试」
  │    │    └─ 否 → 继续
  ├─ needCaptcha(username)?
  │    └─ 是 → 校验验证码
  ├─ 校验账号密码
  │    ├─ 密码正确
  │    │    ├─ onLoginSuccess → 清除 fail key + lock key
  │    │    ├─ evaluatePostLoginPolicy → 评估密码是否符合当前策略
  │    │    │    ├─ 符合 → 正常登录,签发 Token
  │    │    │    └─ 不符合 → 打强制改密标记 → 前端跳转改密页
  │    │    └─ 签发 Token
  │    └─ 密码错误
  │         ├─ onLoginFail(username)
  │         │    ├─ INCR fail key(首次设窗口 TTL)
  │         │    ├─ failCount >= captchaAfterFails → 标记需验证码
  │         │    ├─ failCount >= maxFailCount
  │         │    │    ├─ failLockMinutes > 0 → SET lock key(TTL = failLockMinutes)→ 临时锁定
  │         │    │    └─ failLockMinutes = 0 → lockForPasswordFail → 永久锁定
  │         │    └─ 返回 LoginFailOutcome
  │         └─ 返回错误信息(含验证码 / 锁定提示)
  └─ 前端根据结果展示(验证码 / 锁定倒计时 / 登录成功)

窗口机制: 失败计数的 Redis key 在首次失败时设置过期时间(failWindowMinutes,默认 15 分钟)。如果用户在 15 分钟内没有再失败,计数自动归零。如果持续失败,计数不重置------因为每次 INCR 都不会刷新 TTL,窗口是固定的。

5.3 临时锁定与永久锁定

  • 临时锁定 :达到 maxFailCount 后,在 Redis 中写入 lock key,TTL 为 failLockMinutes 分钟。锁定期间登录直接拒绝,不需要校验密码。锁定到期后自动解除。
  • 永久锁定 :当 failLockMinutes 配置为 0 时,不使用 Redis 临时锁,而是通过 PasswordAccountPort.lockForPasswordFail() 写入数据库持久锁定。永久锁定需要管理员手动解锁。

5.4 登录成功后清理

登录成功时,onLoginSuccess 清除该用户的失败计数和临时锁 key。这保证了用户偶尔输错密码不会累积------只要最终登录成功,计数归零。

5.5 登录后策略评估

登录成功后不只是发 Token------安全模块还会做一次「登录后策略评估」:

复制代码
登录后策略评估(evaluatePostLoginPolicy)
  ├─ 查询用户账号信息
  ├─ 检查是否已有强制改密标记(forcePwdChange)
  ├─ 用当前策略校验本次登录密码
  │    ├─ 校验通过 + 无强制标记 → 正常登录
  │    ├─ 校验不通过 → 打强制改密标记 + 返回改密原因
  │    └─ 已有强制标记 → 直接返回改密原因
  └─ 返回 PostLoginPolicyResult(是否需要改密 + 改密原因)

为什么登录后还要校验密码? 策略是可变的------管理员今天把 minLength 从 8 改成 12,用户原来的 8 位密码就不符合新策略了。登录后评估能发现这种情况,强制用户改密,确保所有在线密码始终符合当前策略。

5.6 强制改密拦截器

当用户被标记为需要强制改密时,ForcePwdChangeInterceptor 会拦截除改密、登出、查询信息之外的所有请求:

bash 复制代码
ForcePwdChangeInterceptor
  ├─ 未登录 → 放行
  ├─ 已登录但未标记强制改密 → 放行
  └─ 已登录且标记强制改密
       ├─ 请求路径在白名单内(login / change-pwd / logout / info 等)→ 放行
       └─ 请求路径不在白名单内 → 抛出 NEED_CHANGE_PWD 异常
            → 前端收到后跳转强制改密页面

这保证了被标记的用户只能改密码或登出,不能访问任何业务功能。

5.7 改密场景与强制标记

写密分三种场景,每种场景对强制改密标记的处理不同:

场景 说明 强制改密标记
FIRST_SET 新增用户时设置初始密码 forceChangeFirst 决定
ADMIN_RESET 管理员重置用户密码 forceChangeReset 决定
SELF_CHANGE 用户自助修改密码 强制设为 false(改完即解除)

管理员重置密码后,用户下次登录时被拦截器拦住,必须先改一个自己记住的新密码才能使用系统。这是常见的安全实践------管理员给的临时密码不应该长期使用。

六、端口-适配器:安全模块如何与业务解耦

6.1 PasswordAccountPort

安全模块需要读写用户账号信息(密码哈希、强制改密标记、锁定状态),但不依赖业务模块的 SysUser 实体。通过 PasswordAccountPort 接口抽象:

  • findByUsername(userId) → 查询账号安全视图
  • updatePassword(userId, hash, force, time) → 更新密码
  • setForcePwdChange(userId, force) → 设置强制改密标记
  • lockForPasswordFail(userId, reason) → 永久锁定

business-system 实现 PasswordAccountPort,内部对接 SysUserSysUserLock 表。安全模块只通过接口操作,不知道也不关心底层表结构。

scss 复制代码
端口-适配器调用关系

  modules-security(安全模块)
    │
    ├── PasswordSecurityService
    │     ├─ validate()           → 校验密码强度
    │     ├─ onLoginFail()        → Redis 计数 + 锁定
    │     └─ onLoginSuccess()     → Redis 清理
    │
    ├── PasswordCredentialService
    │     ├─ prepareNewPassword() → 校验 + BCrypt 哈希
    │     └─ applyNewPassword()   → 校验 + 哈希 + 调 Port 落库
    │
    ├── LoginSecurityFacade
    │     ├─ assertNotTempLocked() → 查 Redis lock
    │     ├─ onBadCredentials()    → 调 onLoginFail + 永久锁定
    │     └─ evaluatePostLoginPolicy() → 校验密码 + 打标记
    │
    └── ForcePwdChangeInterceptor
          └─ accountPort.isForcePwdChange() → 拦截请求
                  │
                  ▼  (接口边界)

  business-system(业务模块)
    │
    ├── PasswordAccountPort 实现
    │     ├─ findById()           → 查 sys_user
    │     ├─ updatePassword()     → 更新 sys_user.password
    │     ├─ setForcePwdChange()  → 更新 sys_user.force_pwd_change
    │     └─ lockForPasswordFail()→ 插入 sys_user_lock
    │
    └── PasswordPolicyProvider 实现
          └─ getCurrent()         → 查三表 + 组装快照 + 缓存

6.2 ObjectProvider 的柔性注入

modules-security 在注入 PasswordPolicyProviderPasswordAccountPort 时使用 ObjectProvider 而非直接 @Autowired

  • 如果 business-system 模块存在(正常部署场景),Provider 和 Port 都可用,安全功能完整运行。
  • 如果 business-system 模块不存在(比如安全模块被其他项目复用),安全模块不会启动失败,只是策略校验功能降级(无策略 → 直接通过)。

这种柔性设计让安全模块可以被任何项目复用,只需要业务端实现对应的 Port 即可。

七、总结与思考

做对了什么

  1. 策略快照 + 本地缓存 + 主动失效。三表联合加载后缓存为一个整体,配置修改后主动清除缓存即时生效。既保证了性能(每次校验不查库),又保证了实时性。

  2. 端口-适配器彻底解耦 。安全模块通过 PasswordAccountPort 操作账号,不依赖任何业务实体。安全模块可以被任何项目复用,业务端只需要实现 Port 接口。

  3. 弱口令精确匹配而非模糊包含。避免了「密码包含弱口令子串」的误伤,只拦截整体等于弱口令的情况,体验和安全兼顾。

  4. 登录后策略评估。策略升级后,老密码不符合新策略的用户会在下次登录时被发现,强制改密。不会出现「策略改了但老密码一直能用」的安全漏洞。

  5. 扩展规则 JSON 配置 + 安全兜底keyboard_seq 等规则支持自定义 JSON 配置,解析失败时回退默认值。既灵活又健壮。

  6. ObjectProvider 柔性注入。安全模块不强制依赖业务模块的存在,可独立复用。

可以改进的地方

  1. 历史密码只存当前一条 。当前 PasswordAccountPort.updatePassword 只传入当前密码哈希,历史密码校验只能比一条。要真正支持 historyCount=3,需要单独维护一张密码历史表,每次改密时插入旧密码哈希。

  2. 密码过期提醒未实现 。策略中有 expireDaysexpireWarnDays 字段,但当前没有定时任务检查密码是否即将过期。需要在定时任务模块中加一个每日扫描,对即将过期的用户通过 SSE 推送提醒。

  3. 临时密码功能未完整tempPwdExpireHours 字段已定义,但临时密码的生成、分发、过期检查流程尚未完整实现。后续需要配合通知模块(邮件/短信)完成。

  4. 验证码触发后未持久化 。当前 captchaAfterFails 只在 Redis 中计数,如果 Redis 重启,计数归零。对于安全要求更高的场景,可以考虑将失败计数持久化到数据库。

  5. 渐进式锁定与一次性锁定的关系不清晰progressive_lock 扩展规则和策略主表的 maxFailCount + failLockMinutes 是两套独立的逻辑,当前 progressive_lock 规则虽然定义了配置结构,但安全模块的 onLoginFail 仍然走的是主表的一次性锁定逻辑。后续需要让 progressive_lock 规则真正参与登录失败处置。


2026 年 8 月 · 第四篇

相关推荐
Lyy5 小时前
DevOps平台 — 第二篇:登录认证与用户角色管理
devops
终末圆6 小时前
GitHub Actions 实现 CI/CD
运维·ci/cd·github·运维开发·devops
运维开发那些事3 天前
GitOps最佳实践 (gitlab ci + ArgoCD)
ci/cd·devops
可乐ea3 天前
Anthropic 的 CI/CD 值班智能体:Claude Tag 当一线响应者的架构拆解与踩坑复盘
ci/cd·架构·claude·devops·ai智能体·mcp
Zadig4 天前
Zadig 全面支持 CRD,至此所有 K8s 资源类型均可一键发布!
后端·devops
NineData9 天前
DTCC 2026 预告|NineData CEO& 创始人叶正盛:面向 AI Agent 的数据库 DevOps 与数据复制实践
数据库·人工智能·数据库开发·devops·ninedata·数据库技术·dtcc
7177779 天前
如何搭建信创研发交付体系?Gitee DevOps 国产化适配、安全与流水线能力解析
安全·gitee·devops
m0_5257247210 天前
AIOps全链路智能运维架构揭秘
云原生·devops