Java使用@Idempotent注解处理幂等问题,防止二次点击

摘要:
Java使用自定义注释@Idempontent来处理幂等性。防止幂等性被单击两次的原则是使用面向AOP方面的编程,在执行业务逻辑之前插入一个方法,生成一个令牌,将其存储在redis中,将其插入到响应中,并将其返回到前台。然后前台使用令牌启动请求。判断后,只执行第一个请求,所有额外点击请求都被阻止。创建自定义注释@Idempentpackageorg。jeecg。普通.不

Java使用自定义注解@Idempotent处理幂等问题,防止二次点击

幂等实现原理就是利用AOP面向切面编程,在执行业务逻辑之前插入一个方法,生成一个token,存入redis并插入到response中返回给前台,

然后前台再拿着这个token发起请求,经过判断,只执行第一次请求,多余点击的请求都拦截下来.

创建自定义注解@Idempotent

package org.jeecg.common.annotation;

import java.lang.annotation.*;

//注解信息会被添加到Java文档中
@Documented
//注解的生命周期,表示注解会被保留到什么阶段,可以选择编译阶段、类加载阶段,或运行阶段
@Retention(RetentionPolicy.RUNTIME)
//注解作用的位置,ElementType.METHOD表示该注解仅能作用于方法上
@Target(ElementType.METHOD)
public @interface Idempotent {
}

创建自定义注解@IdempotentToken

package org.jeecg.common.annotation;

import java.lang.annotation.*;

//注解信息会被添加到Java文档中
@Documented
//注解的生命周期,表示注解会被保留到什么阶段,可以选择编译阶段、类加载阶段,或运行阶段
@Retention(RetentionPolicy.RUNTIME)
//注解作用的位置,ElementType.METHOD表示该注解仅能作用于方法上
@Target(ElementType.METHOD)
public @interface IdempotentToken {
}

@Idempotent注解的配置类 IdempotentInterceptor

package org.jeecg.config.idempotent;

import cn.hutool.core.util.ObjectUtil; import lombok.extern.slf4j.Slf4j; import org.apache.commons.lang3.StringUtils; import org.jeecg.common.annotation.Idempotent; import org.jeecg.common.annotation.IdempotentToken; import org.jeecg.common.util.RedisUtil; import org.springframework.stereotype.Component; import org.springframework.web.method.HandlerMethod; import org.springframework.web.servlet.HandlerInterceptor; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.lang.reflect.Method; import java.util.UUID; /** * @author zbw */ @Slf4j @Component public class IdempotentInterceptor implements HandlerInterceptor { private static final String VERSION_NAME = "version"; private static final String TOKEN_NAME = "idempotent_token"; private static final String IDEMPOTENT_TOKEN_PREFIX = "idempotent:token:"; /*private RedisTemplate<String, Object> redisTemplate;*/ private RedisUtil redisUtil; public IdempotentInterceptor(RedisUtil redisUtil){ this.redisUtil = redisUtil; } @Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { if (!(handler instanceof HandlerMethod)) { return true; } HandlerMethod handlerMethod = (HandlerMethod) handler; Method method = handlerMethod.getMethod(); IdempotentToken idempotentTokenAnnotation = method.getAnnotation(IdempotentToken.class); if(idempotentTokenAnnotation!=null){ //重新更新token String token = UUID.randomUUID().toString().replaceAll("-",""); response.addHeader(TOKEN_NAME,token); //解决后端传递token前端无法获取问题 response.addHeader("Access-Control-Expose-Headers",TOKEN_NAME); redisUtil.set(IDEMPOTENT_TOKEN_PREFIX+token,token); } Idempotent idempotentAnnotation = method.getAnnotation(Idempotent.class); if(idempotentAnnotation!=null){ checkIdempotent(request); } return true; } @Override public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception { } private void checkIdempotent(HttpServletRequest request) { //首先到request中去拿TOKEN_NAME String token = request.getHeader(TOKEN_NAME); if(ObjectUtil.isEmpty(token)){ token = ""; } if (StringUtils.isBlank(token)) {// header中不存在token token = request.getParameter(TOKEN_NAME); if (StringUtils.isBlank(token)) {// parameter中也不存在token throw new IllegalArgumentException("幂等token丢失,请勿重复提交"); } } if (!redisUtil.hasKey(IDEMPOTENT_TOKEN_PREFIX+token)) { throw new IllegalArgumentException("请勿重复提交"); } boolean bool = redisUtil.delete(IDEMPOTENT_TOKEN_PREFIX+token); if(!bool){ throw new IllegalArgumentException("没有删除对应的token"); } } }

这个注解配置是基于redis的,在这里redis的配置略过,本章只是讲解如何简单的使用

 后端的话,需要在该页面的list查询上加上@IdempotentToken注解,原理是涉及到查询成功后会刷新一下页面,重新获取token

@Idempotent注解配置完成就可以直接加在controller的对应方法上,如图:

 Java使用@Idempotent注解处理幂等问题,防止二次点击第1张

 前端的一些配置(这里我用的是ant design vue):

user.js里面加入

idempotentToken:''

SET_IDEMPOTENT_TOKEN:(state,token)=>{
  state.idempotentToken = token
},


getter.js里面加入

idempotentToken: state => state.user.idempotentToken,

request.js里面修改

// request interceptor
service.interceptors.request.use(config => {
  const token = Vue.ls.get(ACCESS_TOKEN)
  if (token) {
    config.headers[ 'X-Access-Token' ] = token // 让每个请求携带自定义 token 请根据实际情况自行修改
  }
  const idempotent_token = Vue.ls.get(IDEMPOTENT_TOKEN)
  if(idempotent_token){
    config.headers['idempotent_token'] = idempotent_token
  }
  //update-begin-author:taoyan date:2020707 for:多租户
  let tenantid = Vue.ls.get(TENANT_ID)
  if (!tenantid) {
    tenantid = 0;
  }
  config.headers[ 'tenant_id' ] = tenantid
  //update-end-author:taoyan date:2020707 for:多租户
  if(config.method=='get'){
    if(config.url.indexOf("sys/dict/getDictItems")<0){
      config.params = {
        _t: Date.parse(new Date())/1000,
        ...config.params
      }
    }
  }
  return config
},(error) => {
  return Promise.reject(error)
})

// response interceptor
service.interceptors.response.use((response) => {
  let idempotent_token = response.headers[IDEMPOTENT_TOKEN]
  if(idempotent_token){
    Vue.ls.set(IDEMPOTENT_TOKEN,idempotent_token,7 * 24 * 60 * 60 * 1000)
    store.commit('SET_IDEMPOTENT_TOKEN', idempotent_token)
  }
    return response.data
  }, err)

const installer = {
  vm: {},
  install (Vue, router = {}) {
    Vue.use(VueAxios, router, service)
  }

mutation-type.js加入定义的常量

export const IDEMPOTENT_TOKEN='idempotent_token'

 这样算是完成了,再次出现二次请求时直接会被拦截。

免责声明:文章转载自《Java使用@Idempotent注解处理幂等问题,防止二次点击》仅用于学习参考。如对内容有疑问,请及时联系本站处理。

上篇JMeter webSocket协议Linux终端(三)下篇

宿迁高防,2C2G15M,22元/月;香港BGP,2C5G5M,25元/月 雨云优惠码:MjYwNzM=

相关文章

JavaWeb response对象常用操作

  JavaWeb response对象常用操作 CreationTime--2018年7月18日10点42分 Author:Marydon 1.设置响应内容类型   方式一 response.setContentType(contentType);   方式二 response.setHeader("Content-Type", contentTy...

Elasticsearch 查看token分析过程

有时候我们在使用Es作为搜索分析的时候,得出的结果并不是我们想要的,这时候我们会查询一下Es会怎么拆解我们的字符,这里就可以用到Es的_analyze查看一下分解的token。 使用教程 GET /_analyze? { "analyzer": "standard", "text": "orJ2t4r8Rlgz-988Y947mMas5zuU"...

蛋疼的haproxy,原来你是这样支持请求转发的_阿福的技术BLOG_百度空间

蛋疼的haproxy,原来你是这样支持请求转发的_阿福的技术BLOG_百度空间 蛋疼的haproxy,原来你是这样支持请求转发的 2010-12-07 19:52 在一个WEB环境中,根据path区分静态文件和动态数据的请求,并把静态文件和CGI放在不同的服务器上。 后来,蛋疼的现象出现了:CGI服务器上发现了对静态文件的请求!!! GouRiDe的...

python-Mitmproxy抓包

一、使用 安装pip install mitmproxymitmproxy 是具有控制台界面的交互式,支持SSL的拦截代理mitmdump是mitmproxy的命令行版本。想想tcpdump为HTTPmitmweb 是一个基于web的界面,适用于mitmproxymitmproxy(mac)、mitmdump、mitmweb(win) 这三个命令中的任意一...

微信公众平台消息接口PHP版

使用前提条件:拥有一个公网上的HTTP服务器主机空间,具有创建目录、上传文件等权限。推荐新浪的SAE。http://sae.sina.com.cn/ 首先请注册微信公众平台的账号,注册地址:http://mp.weixin.qq.com/ 本教程主要讲解接口的开发流程。 官方的文档非常简洁:http://mp.weixin.qq.com/cgi-bin/i...

微信公众平台的开发过程及其要点

微信公众平台的搭建,首先需要确定整体的架构模式,我们一般选用B/S的整体架构模式,手机或者pc端可以通过关注微信公众 号,进而访问和操作公众号中的内容,其中的主要搭建包括: A.使用xampp继承开发环境来搭建后台,需要用到的服务器和数据库。(apache+mysql) B.前端页面通过html+php动态网页来实现。如果再加点js+css效果就显示的更加...