接口幂等性校验

接口幂等性校验接口幂等性校验 nbsp 一 概念幂等 idempotent idempotence 是一个数学与计算机学概念 常见于抽象代数中 在编程中 一个幂等操作的特点是其任意多次执行所产生的影响均与一次执行的影响相同 幂等函数 或幂等方法 是指可以使用相同参数重复执行 并能获得相同结果的函数 这些函数不会影响系统状态 也不用担心重复执行会对系统造成改变 例如 getUsername 和 set

接口幂等性校验

 

一、概念

幂等(idempotent、idempotence)是一个数学与计算机学概念,常见于抽象代数中。

在编程中.一个幂等操作的特点是其任意多次执行所产生的影响均与一次执行的影响相同。幂等函数,或幂等方法,是指可以使用相同参数重复执行,并能获得相同结果的函数。

这些函数不会影响系统状态,也不用担心重复执行会对系统造成改变。例如,“getUsername()和setTrue()”函数就是一个幂等函数.

幂等性, 通俗的说就是一个接口, 多次发起同一个请求, 必须保证操作只能执行一次,比如:

  • 订单接口, 不能多次创建订单
  • 支付接口, 重复支付同一笔订单只能扣一次钱
  • 支付宝回调接口, 可能会多次回调, 必须处理重复回调
  • 普通表单提交接口, 因为网络超时等原因多次点击提交, 只能成功一次
    等等

 

二、常见解决方案

  • 唯一索引 – 防止新增脏数据
  • token机制 – 防止页面重复提交
  • 悲观锁 – 获取数据的时候加锁(锁表或锁行)
  • 乐观锁 – 基于版本号version实现, 在更新数据那一刻校验数据
  • 分布式锁 – redis(jedis、redisson)或zookeeper实现
  • 状态机 – 状态变更, 更新数据时判断状态

 

三、本文实现方案

本文采用第2种方式实现, 即通过redis + token机制实现接口幂等性校验

为需要保证幂等性的每一次请求创建一个唯一标识token, 先获取token, 并将此token存入redis, 请求接口时, 将此token放到header或者作为请求参数请求接口, 后端接口判断redis中是否存在此token,如果存在, 正常处理业务逻辑, 并从redis中删除此token, 那么, 如果是重复请求, 由于token已被删除, 则不能通过校验, 返回请勿重复操作提示,如果不存在, 说明参数不合法或者是重复请求, 返回提示即可

 

四、核心代码

依赖库

dependencies { implementation 'org.springframework.boot:spring-boot-starter-data-redis' implementation 'org.springframework.boot:spring-boot-starter-web' implementation group: 'org.apache.commons', name: 'commons-lang3', version: '3.9' implementation 'org.projectlombok:lombok' testImplementation 'org.springframework.boot:spring-boot-starter-test' } 

自定义注解

/ * 在需要保证 接口幂等性 的Controller的方法上使用此注解 */ @Target({ 
   ElementType.METHOD}) @Retention(RetentionPolicy.RUNTIME) public @interface ApiIdempotent { 
    } 

TokenServiceImpl

/ * token业务处理,提供token创建、token验证接口 * Created by double on 2019/7/11. */ @Service public class TokenServiceImpl implements TokenService { 
    private static final String TOKEN_NAME = "token"; @Autowired StringRedisTemplate stringRedisTemplate; @Override public ServerResponse createToken() { 
    //通过UUID来生成token String tokenValue = "idempotent:token:" + UUID.randomUUID().toString(); //将token放入redis中,设置有效期为60S stringRedisTemplate.opsForValue().set(tokenValue, "0", 60, TimeUnit.SECONDS); return ServerResponse.success(tokenValue); } / * @param request */ @Override public void checkToken(HttpServletRequest request) { 
    String token = request.getHeader(TOKEN_NAME); if (StringUtils.isBlank(token)) { 
    token = request.getParameter(TOKEN_NAME); if (StringUtils.isBlank(token)) { 
    //没有携带token,抛异常,这里的异常需要全局捕获 throw new ServiceException(ResponseCode.ILLEGAL_ARGUMENT.getMsg()); } } //token不存在,说明token已经被其他请求删除或者是非法的token if (!stringRedisTemplate.hasKey(token)) { 
    throw new ServiceException(ResponseCode.REPETITIVE_OPERATION.getMsg()); } boolean del = stringRedisTemplate.delete(token); if (!del) { 
    //token删除失败,说明token已经被其他请求删除 throw new ServiceException(ResponseCode.REPETITIVE_OPERATION.getMsg()); } } } 

IdempotentTokenInterceptor

/ * 接口幂等性校验拦截器 * Created by double on 2019/7/11. */ public class IdempotentTokenInterceptor implements HandlerInterceptor { 
    @Autowired private TokenService tokenService; @Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) { 
    if (!(handler instanceof HandlerMethod)) { 
    return true; } HandlerMethod handlerMethod = (HandlerMethod) handler; //幂等性校验, 校验通过则放行, 校验失败则抛出异常, 并通过统一异常处理返回友好提示 ApiIdempotent apiIdempotent = handlerMethod.getMethod().getAnnotation(ApiIdempotent.class); if (apiIdempotent != null) { 
    tokenService.checkToken(request); } return true; } @Override public void postHandle(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o, ModelAndView modelAndView) throws Exception { 
    } @Override public void afterCompletion(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o, Exception e) throws Exception { 
    } } 

完成代码参考我的github:https://github.com/huchao1009/idempotent.git

 

五、测试

测试接口Controller

/ * 幂等性测试接口 * Created by double on 2019/7/11. */ @RestController @RequestMapping("/test") public class TestController { 
    @Autowired private TestService testService; @ApiIdempotent @PostMapping("testIdempotent") public ServerResponse testIdempotent() { 
    return testService.testIdempotence(); } } 

 

1、获取token
http://localhost:8081/token 
{ 
    "status": 0, "msg": "idempotent:token:80dd47ed-fa63-4b30-82fa-f3ee4fd64a50", "data": null } 

 

2、验证接口安全性
http://localhost:8081/test/testIdempotent?token=idempotent:token:b9ae797d-ed1a-4dbc-a94f-b7e45897f0f5 

第一次请求

{ 
    "status": 0, "msg": "test idempotent success", "data": null } 

 

重复请求

{ 
    "status": 1, "msg": "请勿重复操作", "data": null } 

 

3、利用jmeter测试工具模拟50个并发请求, 获取一个新的token作为参数

设置50个线程请求一次

image-20200415170757239

设置请求IP、Path、参数等信息image-20200415170807474

查看执行结果,可以看到只有一个请求成功,其他的请求都返回错误

image-20200415170713970

image-20200415170741939

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请联系我们举报,一经查实,本站将立刻删除。

发布者:全栈程序员-站长,转载请注明出处:https://javaforall.net/223510.html原文链接:https://javaforall.net

(0)
上一篇 2026年3月17日 下午2:02
下一篇 2026年3月17日 下午2:02


相关推荐

  • Python 将json字幕转换歌词lrc格式

    Python 将json字幕转换歌词lrc格式Python 转换 json 字幕为 lrc 歌词

    2026年3月17日
    2
  • java中jbpm工作流_node 工作流引擎

    java中jbpm工作流_node 工作流引擎1.      JBPM工作流引擎是用来做什么的首先要说明的一点是工作流引擎指的并不只是JBPM,JBPM只是工作流引擎的一种。JBPM利用JPDL流程定义语言将现实生活中处理事务的业务流程进行抽象,形成一套业务流程规则,只要处理该项业务就必须按照这个流程规则进行。举一个很简单的例子,就拿看医生来讲,看医生的整个流程必须是先挂号,再看病,再抓药,只要你进行看医生这个业务就必须按照这套流程进行。

    2025年10月14日
    5
  • webmagic ajax,webmagic 爬虫 分页

    webmagic ajax,webmagic 爬虫 分页importus codecraft webmagic Page importus codecraft webmagic Site importus codecraft webmagic Spider importus codecraft webmagic processor PageProcesso importus codecraft webmagic selector JsonP

    2025年8月15日
    5
  • python关机程序代码_python实现的重启关机程序实例

    python关机程序代码_python实现的重启关机程序实例本文实例讲述了Python实现的重启关机程序的方法,对Python程序设计有一定的参考价值。具体方法如下:实例代码如下:#!/usr/bin/python#coding=utf-8importtimefromosimportsystemruning=Truewhileruning:input=raw_input(‘关机(s)OR重启(r)?(q退出)‘)input=input…

    2022年7月22日
    11
  • android高德地图中心点,高德地图中心点以及自定义infowindow[通俗易懂]

    android高德地图中心点,高德地图中心点以及自定义infowindow[通俗易懂]jdfw.gif基本效果图就是这个样子,录制这个软件不太好使,每次切换地点是点击确定变更的。接下来就看看地图上的功能是如何实现的:实现的方式编写自定义的infowindow一,书写布局样式(自定义随意写)android:layout_width=”match_parent”android:layout_height=”match_parent”android:background=”@drawab…

    2022年5月18日
    46
  • PCRE 简介

    PCRE 简介介绍 PCRE nbsp PerlCompatib 是一个用 C 语言编写的正则表达式函数库 本文简要介绍 PCRE 的编译和使用方法 编译从 PCRE 官网下载后 解压 本人下的版本为 pcre2 10 21 仔细阅读 README 和 NON AUTOTOOLS BUILD 里面介绍了很多编译的方法 由于本人需要在 VS 下开发 因此选择 CMake 编译 nbsp 1

    2026年3月19日
    2

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

关注全栈程序员社区公众号