当前位置: 首页 > news >正文

Spring Boot 接口幂等实战:用 Token + Redis 防重复提交

Spring Boot 接口幂等实战:用 Token + Redis 防重复提交

用户手抖双击了「提交订单」,或者网络卡了页面转圈他又点一次,结果后台生成了两条一模一样的订单。这是几乎每个交易类接口都要面对的问题:同一个业务操作被重复执行。前端加个 loading 禁用按钮只能挡君子,挡不住重发请求、挡不住网络重试、更挡不住恶意刷单。

真正靠谱的做法是让接口本身幂等——同一个请求打多少次,结果都只生效一次。这篇用最常见的「Token + Redis」方案手把手实现一遍。

先看朴素做法为什么不够

第一反应可能是「查一下数据库有没有相同订单」:

// ❌ 查重再插入,并发下依然会重复if(orderMapper.findByBizId(bizId)==null){orderMapper.insert(order);}

问题在于「查」和「插」是两步,不是原子的。两个并发请求可能同时查到「没有」,然后同时插入,查重形同虚设。加数据库唯一索引能兜底,但那是最后一道防线,还会给库带来大量重复写压力。我们要在更前面把重复请求挡掉。

方案思路:一次性 Token

核心是「令牌只能用一次」:

  1. 用户进入表单页时,先向后端申请一个唯一 Token,后端把它存进 Redis 并返回给前端。
  2. 提交表单时,前端把 Token 放在请求头里带上。
  3. 后端校验:Token 存在就原子地删除它并放行;不存在(已被删)就是重复提交,拒绝。

关键就在第 3 步的「校验并删除」必须是原子操作,否则并发下又会出现两个请求都查到 Token 还在、都通过的情况。

第一步:提供获取 Token 的接口

@RestController@RequestMapping("/api/idempotent")publicclassTokenController{@ResourceprivateStringRedisTemplateredis;privatestaticfinalStringPREFIX="idem:token:";@GetMapping("/token")publicMap<String,String>getToken(){Stringtoken=UUID.randomUUID().toString();// 存 Redis 并设过期,防止申请了不用堆积垃圾 keyredis.opsForValue().set(PREFIX+token,"1",5,TimeUnit.MINUTES);returnMap.of("token",token);}}

Token 一定要设过期时间,用户申请了不提交是常态,不能让它永久占内存。

第二步:用 Lua 脚本保证「校验+删除」原子

先看错误写法,分两步会有并发漏洞:

// ❌ get 和 delete 之间有窗口,两个请求可能都 get 到 "1"if(redis.opsForValue().get(key)!=null){redis.delete(key);// 放行……但可能已经有另一个请求也走到这}

正确做法是用 Lua 脚本把「判断存在 + 删除」放在 Redis 里一次执行,Redis 单线程执行脚本天然原子:

@ComponentpublicclassIdempotentChecker{@ResourceprivateStringRedisTemplateredis;// KEYS[1] 存在则删除并返回 1,不存在返回 0privatestaticfinalStringLUA="if redis.call('get', KEYS[1]) then "+" return redis.call('del', KEYS[1]) "+"else return 0 end";privatefinalDefaultRedisScript<Long>script=newDefaultRedisScript<>(LUA,Long.class);/** 返回 true 表示这是首次、放行;false 表示重复提交 */publicbooleancheck(Stringtoken){Longr=redis.execute(script,Collections.singletonList("idem:token:"+token));returnr!=null&&r>0;}}

第三步:用注解 + 拦截器优雅接入

不想每个接口都手写校验,可以做个注解,靠 AOP 统一拦:

@Target(ElementType.METHOD)@Retention(RetentionPolicy.RUNTIME)public@interfaceIdempotent{}
@Aspect@ComponentpublicclassIdempotentAspect{@ResourceprivateIdempotentCheckerchecker;@Around("@annotation(idempotent)")publicObjectaround(ProceedingJoinPointpjp,Idempotentidempotent)throwsThrowable{HttpServletRequestreq=((ServletRequestAttributes)RequestContextHolder.currentRequestAttributes()).getRequest();Stringtoken=req.getHeader("Idempotent-Token");if(token==null||!checker.check(token)){// Token 缺失或已被用过,判定为重复提交thrownewIllegalStateException("请勿重复提交");}returnpjp.proceed();}}

业务接口只需一个注解:

@PostMapping("/order")@Idempotent// 加上它,重复提交自动被挡publicOrdercreateOrder(@RequestBodyOrderReqreq){returnorderService.create(req);}

几个容易忽略的坑

  • Token 校验要放在业务执行之前。如果先执行业务再删 Token,业务耗时期间的重复请求还是会漏进来。
  • 过期时间要覆盖真实操作时长。用户填表可能填很久,5 分钟太短会误伤,按业务合理设置。
  • Token 方案挡的是「重复提交」,不是「重复请求同一资源」。对于「同一笔支付回调可能被支付方重推多次」这种,业务方没法帮你申请 Token,应该用业务唯一键(如支付流水号)做幂等,思路一样:拿业务键去 RedissetIfAbsent,成功才处理。
  • 数据库唯一索引仍要保留。Redis 挂了、key 被误删都可能让防线失效,唯一索引是最后的兜底。

小结

Token + Redis 幂等方案的要点:

  • 进页面先申请一次性Token 存 Redis,提交时带回来。
  • 校验用Lua 脚本原子地「判断+删除」,别用 get 完再 delete,那有并发窗口。
  • 注解 + AOP统一接入,业务代码零侵入。
  • Redis 是第一道防线,数据库唯一索引是最后兜底,两者都要有。

一句话记忆点:幂等的本质是「让重复请求识别得出、且只有一个能通过」,而"识别+通过"这一步必须原子——Lua 脚本就是干这个的。

http://www.jsqmd.com/news/1250603/

相关文章:

  • Micrometer 系列【1】JVM 可观测门面框架全景概述
  • 江诗丹顿中国售后服务中心|电话及维修地址权威信息公告(2026年7月最新) - 江诗丹顿服务中心
  • 2026年7月专业的室内墙面装修材料厂商推荐,旧墙翻新艺术涂料/微水泥艺术涂料,室内墙面装修材料厂商口碑推荐 - 品牌推荐师
  • 20260722 4(+1) 85 18
  • 保姆级教程:MCP 工具链搭建实战——从零配置 AI 编程助手
  • 南昌觅食指南:食材新鲜的重庆火锅门店场景盘点 - 品牌2026推荐
  • Pulsar 消息同步机制
  • 矿山破碎设备液压监测采购,液压传感器十大厂家推荐,广东犸力靠谱使用寿命更长 - 品牌速递
  • APS 需求计划(Demand Planning)技术拆解
  • Go http.Client 实战:连接池复用、超时设置与 context 取消
  • HarmonyOS7 数据持久化:Preferences 和 RDB 到底选哪个?
  • 广州财税服务行业GEO优化公司选型指南丨生成式引擎优化服务商深度测评2026本地榜单解析 - 企业新闻快传
  • 59.嵌入式C语言高级宏定义实战:多行宏、字符串化与符号拼接
  • 【RT-DETR涨点改进】TGRS 2026 | 特征融合改进篇 |引入CDSF跨域协同融合模块,增强特征互补性与语义一致性,助力高光谱目标检测、遥感目标检测、多模态融合目标检测任务,高效涨点
  • NTC 热敏电阻全解:计算、曲线读数、跨品牌替换一次讲透
  • 2026年陕西学化妆选校攻略:正规权威机构甄别与避坑指南 - 产业观察报
  • 2026 年现阶段泾县评价高的膜结构张拉膜施工厂家推荐公司怎么联系,颠覆认知:膜结构张拉,比你想象的更省钱! - 行业鉴选官
  • 椰林海鲜码头企业新愿景是什么?:良正基业永昌 - 18002239949
  • 智能装备整机采购指南,2026转矩传感器品牌排行更新,广东犸力销量排名逐年攀升 - 品牌速递
  • ESC框架知识点
  • D2D商业化初期的博弈:成本、价格与增长的“三重奏”
  • 沁园春·数智潮
  • 汽车轮重检测仪品牌排名汇总揭晓,浙江润鑫头部品牌一致好评实力获市场认可 - 品牌速递
  • 老板必看!AI落地就做这两件事,ROI瞬间翻倍!
  • 联合利华CIO任命揭示企业数字化转型战略
  • 多元微积分
  • 【RT-DETR涨点改进】CVPR 2026 | 卷积创新改进篇 | 轻量化改进!引入YOLO-ULM中轻量D3C2f轻量化模块,含二次创新模块,5种创新改进点,助力目标检测任务,高效涨点
  • 浪琴天津售后服务中心2026年7月最新地址与客服热线重磅发布 - 浪琴服务中心
  • 2026西安美甲创业培训学校选型盘点及避坑指南:哪家正规靠谱且适配本地创业需求?附正规机构实力解析 - 行业观察网
  • LLM-Pruner: On the Structural Pruning of Large Language Models 解读