当前位置:网站首页>接口幂等性问题
接口幂等性问题
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();
}
边栏推荐
- 类和对象【中】
- First acquaintance with C language (1)
- 【计算机三级信息安全】信息安全保障概述
- HDU 1435 stable match
- Advanced assignment method of ES6 -- Deconstruction assignment
- 从微服务基本概念到核心组件-通过一个实例来讲解和分析
- Anaconda common instructions
- Simulink automatically generates STM32 code details
- Gan: generative advantageous nets -- paper analysis and the mathematical concepts behind it
- Dell remote control card uses ipmitools to set IPMI
猜你喜欢
Printf function of input and output function in C language

Driving the powerful functions of EVM and xcm, how subwallet enables Boca and moonbeam

How does Alibaba use DDD to split microservices?

Know etcd

类和对象【中】

Professor dongjunyu made a report on the academic activities of "Tongxin sticks to the study of war and epidemic"

【CPU占用高】software_reporter_tool.exe

【ARXIV2205】Inception Transformer

Online sql to XML tool

Reading notes of SMT practical guide 1
随机推荐
Clickhouse pit filling note 2: the join condition does not support non equal judgments such as greater than and less than
Check box error
【ARXIV2204】Vision Transformers for Single Image Dehazing
MySQL date and time function, varchar and date are mutually converted
【ARXIV2205】Inception Transformer
Scope, execution process and life cycle of bean
ES6 new variable modifiers let and const, new basic data type symbol
C language characters and strings
Online sql to XML tool
HDU 3666 the matrix problemdifferential constraint + stack optimization SPFA negative ring
Share several methods of managing flag bits in C program
Specific differences between typedef and define
Simulink automatically generates STM32 code details
PC端-bug记录
【CVPR2022】On the Integration of Self-Attention and Convolution
2022 summer practice (PowerDesigner tutorial learning record) (first week)
Advanced assignment method of ES6 -- Deconstruction assignment
Tips for using swiper (1)
凛冬已至,程序员该怎么取暖
Pipe /createpipe