当前位置:网站首页>接口幂等性问题
接口幂等性问题
2022-07-28 05:16:00 【小疯子青】
实际项目或者面试的时候经常会问到如何防止接口幂等性,故此对本人接触过的方法进行总结。
1.使用redis进行延时拦截
让前端传送必要信息以及时间戳然后存入redis,利用redis的超时机制来拦截重复请求,这个方法也是我问了好几个人之后从他们那里得到的方案,但是经认证该方法是有问题的,发现还是会出现重复传送的情况,因此该方案在项目中没有使用。
2.从业务上进行拦截
指的是在一定的时间内或者是在某些条件之下,只允许发送一次修改或者新增的请求,例如购买虚拟货币,在平台没有发配货币之前不允许再进行购买,这个可以通过状态判断来决定是否允许重复操作,或者是在一定的时间内只允许操作一次,这个可以通过redis的超时机制+操作人唯一id+操作类型来限制,这种方案在实际使用中是可行的,即使存在前端网络抖动多次操作,也只会有一个操作成功,但是仅适用于特性的业务场景。
3.临时令牌的方式
对于由于前端或者网络原因导致的多次请求,我们可以在请求操作接口之前让前端请求一个一次性的令牌,当发出多次请求的时候,如果接口上带的令牌都是一个令牌,那么仅会有一个操作成功,这样可以防止网络或者前端多次触发请求导致的重复操作问题,但是这个方式无法应对”请求token–请求操作接口“捆绑操作的重发问题,目前在实际项目中使用该方式没有重复下单的问题。
以下为方案代码:
/** * 防重复提交的注解 * 放在Controller类:表示当前类的所有接口需要考虑接口幂等性 * 放在方法上:表示当前方法需要考虑接口幂等性 */
@Target({
ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface RepeatLimiter {
}
注解的拦截
@Component
@Slf4j
public class TokenInterceptor extends HandlerInterceptorAdapter {
@Value("${jwt.secret}")
private String secret;
@Autowired
private TokenService tokenService;
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) {
// 获取方法上的注解
HandlerMethod handlerMethod = (HandlerMethod) handler;
Method method = handlerMethod.getMethod();
PenguinLogThreadLocal.remove();
response.setHeader("Access-Control-Allow-Origin", request.getHeader("Origin"));
response.setHeader("Access-Control-Allow-Credentials", "true");
response.setHeader("P3P", "CP=CAO PSA OUR");
if (request.getHeader("Access-Control-Request-Method") != null && "OPTIONS".equals(request.getMethod())) {
response.addHeader("Access-Control-Allow-Methods", "POST,GET,TRACE,OPTIONS");
response.addHeader("Access-Control-Allow-Headers", "Content-Type,Origin,Accept");
response.addHeader("Access-Control-Max-Age", "120");
}
if (method.isAnnotationPresent(NoVerificationToken.class)) {
return true;
}
//校验用户的合法性
String token = request.getHeader("token");
response.setContentType("text/javascript;charset=UTF-8");
response.setCharacterEncoding("UTF-8");
Outcome<Long> rspBean = GameTokenUtil.checkToken(token,secret);
PenguinLogThreadLocal.appendLog("tokenResult",rspBean);
if (!rspBean.ok()) {
setOutCome(request,response,rspBean,"401");
return false;
}
UserInfoThreadlocal.put(rspBean.getData());
PenguinLogThreadLocal.setUserId(rspBean.getData().toString());
//检验用户合法性
rspBean = tokenService.checkUser(UserInfoThreadlocal.get());
if (!rspBean.ok()) {
setOutCome(request,response,rspBean,"401");
return false;
}
//验证接口是否需要考虑幂等性问题
if(method.isAnnotationPresent(RepeatLimiter.class)){
String repeatLimNo = request.getHeader("repeatLimNo");
Outcome<Long> rspReapBean =tokenService.checkRepeatLimNo(repeatLimNo);
if(!rspReapBean.ok()){
setOutCome(request,response,rspReapBean,"402");
return false;
}
}
return true;
}
private PrintWriter setOutCome(HttpServletRequest request,HttpServletResponse response,Outcome<Long> rspBean,String code){
PrintWriter out = null;
try {
JSONObject res = new JSONObject();
res.put("code",new Long(code));
res.put("message",rspBean.getMessage()+"");
res.put("data","");
PenguinLogThreadLocal.setSuccessful(false);
out = response.getWriter();
out.append(res.toString());
return out;
} catch (IOException e) {
log.error(e.getMessage());
}
return out;
}
}
@PostMapping("/repeatLimNo")
@ApiOperation(value = "获取请求序列", notes = "获取请求序列--拦截重复请求")
public Outcome<String> getToken(@RequestHeader(value = "token") String token, HttpServletRequest request) {
return Outcome.success("", repeatLimiterSeq.generateSeq());
}
token生成
//生成Token
@Override
public String generateSeq() {
//使用UUID生成 token
String token = UUID.randomUUID().toString();
token=token.replaceAll("-","");
//存入Redis,key:token,过期时间10分钟
redisUtil.set("repeatLimNo::"+token,token,20*60);
PenguinLogThreadLocal.setParam(token);
return token;
}
@Override
public Boolean deleteSeq(String repeatLimNo) {
return stringRedisTemplate.delete("repeatLimNo::"+repeatLimNo);
}
@Override
public String getSeq(String repeatLimNo) {
Object temp=redisUtil.get("repeatLimNo::"+repeatLimNo);
if(temp!=null){
return temp.toString();
}
return null;
}
后端使用代码:
@ApiOperation(value = "测试序列号", notes = "测试序列号")
@PostMapping(value = "测试序列号")
@RepeatLimiter
public Outcome testLimiter(@RequestHeader(value = "token") String token, @RequestHeader(value = "repeatLimNo") String repeatLimNo) {
return Outcome.success();
}
边栏推荐
- Jsonp single sign on permission verification
- RT based_ Distributed wireless temperature monitoring system of thread (I)
- MySQL(5)
- 多御安全浏览器将改进安全模式,让用户浏览更安全
- C language classic 100 question exercise (1~21)
- New methods and features of ES6 built-in objects
- After ruoyi generates the code corresponding to the database, what should I do to make the following image look like
- 11.< tag-动态规划和子序列, 子数组>lt.115. 不同的子序列 + lt. 583. 两个字符串的删除操作 dbc
- MySQL(5)
- Confused, I'm going to start running in the direction of [test]
猜你喜欢

FreeRTOS startup process, coding style and debugging method

From the basic concept of micro services to core components - explain and analyze through an example

11.< tag-动态规划和子序列, 子数组>lt.115. 不同的子序列 + lt. 583. 两个字符串的删除操作 dbc

Visual studio 2019 new OpenGL project does not need to reconfigure the environment

测试开发---自动化测试中的UI测试

Eccv2022 | 29 papers of Tencent Youtu were selected, including face security, image segmentation, target detection and other research directions

Melt cloud x chat, create a "stress free social" habitat with sound

【ARXIV2203】SepViT: Separable Vision Transformer

Dcgan:deep volume general adaptive networks -- paper analysis

Flask Development & get/post request
随机推荐
SSLError
在外包公司两年了,感觉快要废了
HDU 2874 connections between cities
Win10 machine learning environment construction pycharm, anaconda, pytorch
[internal mental skill] - creation and destruction of function stack frame (C implementation)
HashSet add
Flink mind map
Gym 101911c bacteria (minimum stack)
What is the reason why the easycvr national standard protocol access equipment is online but the channel is not online?
How practical is the struct module? Learn a knowledge point immediately
Tips for using swiper (1)
SMD component size metric English system corresponding description
Duoyu security browser will improve the security mode and make users browse more safely
类和对象【中】
First acquaintance with C language (1)
MySQL(5)
Offline loading of wkwebview and problems encountered
【ARIXV2204】Neighborhood attention transformer
I interviewed a 38 year old programmer and refused to work overtime
【计算机三级信息安全】信息安全保障概述