如何在使用AWS Cognito的Spring Boot应用中限制每日改密尝试次数为3次
实现思路与落地方案
你可以根据业务复杂度二选一,优先推荐第一种Cognito原生方案,运维成本更低。
方案1:AWS Cognito原生+Lambda触发器实现(无额外存储依赖)
- 先在Cognito用户池新增两个只读自定义属性:
custom:pw_reset_attempts(数字类型,存当日尝试次数)、custom:last_pw_reset_date(字符串类型,存最近一次尝试的日期,格式为yyyy-MM-dd),注意关闭普通用户的属性编辑权限,仅允许后台修改。 - 新建一个Lambda函数,给该函数对应的IAM角色添加
cognito-idp:AdminUpdateUserAttributes权限,用来修改用户的自定义属性。 - 将Lambda绑定到Cognito用户池的「修改密码(ChangePassword)」和「忘记密码(ForgotPassword)」前置触发事件,Lambda核心逻辑如下:
- 每次收到修改密码请求时,先读取当前用户的两个自定义属性值
- 若
last_pw_reset_date不是当日,重置pw_reset_attempts为0,更新last_pw_reset_date为当前日期,放行请求 - 若
last_pw_reset_date是当日,判断pw_reset_attempts是否≥3:大于等于3时直接返回错误终止流程;小于3时给尝试次数+1,放行请求
- 额外补充:如果用户修改密码成功,可以通过后置触发器把尝试次数重置为0,避免不必要的拦截。
方案2:Spring Boot侧自主实现(灵活性更高,适配复杂业务规则)
适合需要和自有业务逻辑深度结合的场景,用Redis做计数存储效率最高:
- 第一步:在Spring Boot项目中引入Redis依赖,配置
StringRedisTemplate实例。 - 第二步:在你封装的修改密码接口前添加拦截逻辑,核心校验代码示例如下:
import java.time.LocalDate; import java.time.LocalDateTime; import java.time.format.DateTimeFormatter; import java.time.temporal.ChronoUnit; import java.util.concurrent.TimeUnit; import org.springframework.data.redis.core.StringRedisTemplate; // 仅返回是否允许尝试修改密码 public boolean isAllowPwReset(String cognitoUserId) { String today = LocalDate.now().format(DateTimeFormatter.ISO_LOCAL_DATE); String countKey = "pw_reset_attempt:" + cognitoUserId + ":" + today; // 计数+1,首次操作自动初始化为1 Long currentAttempt = stringRedisTemplate.opsForValue().increment(countKey, 1); // 首次计数时设置过期时间为当日24点,自动清理过期数据 if (currentAttempt == 1) { long expireSeconds = ChronoUnit.SECONDS.between(LocalDateTime.now(), LocalDate.now().plusDays(1).atStartOfDay()); stringRedisTemplate.expire(countKey, expireSeconds, TimeUnit.SECONDS); } return currentAttempt <= 3; }
- 第三步:如果调用Cognito修改密码接口返回成功,直接删除对应用户的当日计数key,避免后续误拦截。
注意事项
- 必须同时覆盖两类修改密码场景:已登录用户主动修改密码、未登录用户通过忘记密码流程重置密码
- 用Cognito自定义属性时,不要给前端暴露属性的编辑权限,避免用户自行篡改尝试次数
- 用Spring Boot侧方案时,注意Redis的key过期时间要精确到当日结束,不要设置固定24小时过期,避免跨日计数错误
内容的提问来源于stack exchange,提问作者Sonalkumar sute
相关产品推荐
相关产品推荐

