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

AOP实现接口幂等

实现原理:针对控制器的入口进行切面拦截请求参数,对参数进行MD5加密,将密文存储在redis中,若短时间内有相同的参数进行请求时,进行拦截提示

1、切面注解

import java.lang.annotation.*;/*** 防重放操作校验*/
@Documented
@Target({ElementType.FIELD, ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
public @interface RepeatOperationCheck {/*** 是否校验重放操作*/boolean isCheck() default true;}

2、切面

import com.alibaba.fastjson.JSON;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.Signature;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.http.HttpMethod;
import org.springframework.stereotype.Component;
import org.springframework.web.multipart.MultipartFile;import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.lang.reflect.Method;
import java.util.Map;
import java.util.concurrent.TimeUnit;/*** desc 表单操作防重放校验* Author WuYongQiang* Date 2025/3/13 14:53* Param * return **/
@Component
@Aspect
@Slf4j
public class RepeatOperationCheckAspect {@AutowiredHttpServletRequest httpServletRequest;@AutowiredRedisTemplate redisTemplate;/*** 声明切面* 只要Controller的方法中有@log注解就切入 重要*/@Pointcut("@annotation(***.RepeatOperationCheck)")public void logPointCut() {}@Before("logPointCut()")public void doBefore(JoinPoint joinPoint){//1、获得注解RepeatOperationCheck repeatOperationCheck = getAnnotationLog(joinPoint);if (repeatOperationCheck == null) {return;}if (!repeatOperationCheck.isCheck()) {return;}//2、获取请求参数String requestParams = getRequestParams(joinPoint);if(StringUtils.isBlank(requestParams)){return;}//3、将requestParams进行md5加密String value = encrypt(requestParams);//4、获取方法url+String requestUrl = httpServletRequest.getRequestURI();//5、获取redis的keyString key = requestUrl + value;//5、redis校验Boolean b = redisTemplate.opsForValue().setIfAbsent(key, value, 1, TimeUnit.SECONDS);if (!b){throw new RuntimeException("请勿重复操作");}}/*** desc 入参转md5* Author WuYongQiang* Date 2025/3/13 16:57* Param [joinPoint]* return java.lang.String**/private String encrypt(String requestParams){return MD5Util.md5(requestParams);}/*** 是否存在注解,如果存在就获取*/private RepeatOperationCheck getAnnotationLog(JoinPoint joinPoint){Signature signature = joinPoint.getSignature();MethodSignature methodSignature = (MethodSignature) signature;Method method = methodSignature.getMethod();if (method != null) {return method.getAnnotation(RepeatOperationCheck.class);}return null;}/*** 获取请求的参数*/private String getRequestParams(JoinPoint joinPoint){String requestMethod = httpServletRequest.getMethod();String paramsStr = "";if (HttpMethod.PUT.name().equals(requestMethod) || HttpMethod.POST.name().equals(requestMethod)) {paramsStr = argsArrayToString(joinPoint.getArgs());} else {Map<String, String[]> parameterMap = httpServletRequest.getParameterMap();paramsStr = JSON.toJSONString(parameterMap);}return paramsStr;}/*** 参数拼装*/private String argsArrayToString(Object[] paramsArray) {String params = "";if (paramsArray != null && paramsArray.length > 0) {for (int i = 0; i < paramsArray.length; i++) {if (!isFilterObject(paramsArray[i])) {Object jsonObj = JSON.toJSON(paramsArray[i]);params += jsonObj.toString() + " ";}}}return params.trim();}/*** 判断是否需要过滤的对象。** @param o 对象信息。* @return 如果是需要过滤的对象,则返回true;否则返回false。*/public boolean isFilterObject(final Object o) {return o instanceof MultipartFile || o instanceof HttpServletRequest|| o instanceof HttpServletResponse;}}

3、Md5加密工具类

import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;import static java.nio.charset.StandardCharsets.UTF_8;public class MD5Util {/*** 加密算法-md5*/public static final String ALGORITHM = "md5-salt";/*** 对字符串进行MD5加密** @param input 需要加密的字符串* @return 加密后的32位小写MD5字符串*/public static String md5(String input) {try {// 获取MD5算法实例MessageDigest md = MessageDigest.getInstance("MD5");// 将输入字符串转换为字节数组并计算摘要byte[] messageDigest = md.digest(input.getBytes());// 将字节数组转换为十六进制字符串StringBuilder hexString = new StringBuilder();for (byte b : messageDigest) {String hex = Integer.toHexString(0xff & b);if (hex.length() == 1) hexString.append('0');hexString.append(hex);}return hexString.toString();} catch (NoSuchAlgorithmException e) {throw new RuntimeException("MD5算法不可用", e);}}/*** 计算md5值** @param string 加密串* @return md5值* @throws NoSuchAlgorithmException*/public static String md5Hex(String string) throws NoSuchAlgorithmException {StringBuilder md5Hex = new StringBuilder();MessageDigest md5 = MessageDigest.getInstance("MD5");for (byte b : md5.digest(string.getBytes(UTF_8))) {md5Hex.append(String.format("%02X", b));}return md5Hex.toString();}
}

5、调用方式

    @PostMapping("/operator")@RepeatOperationCheckpublic R<Object> test(@RequestBody Dto dto) {//........}
http://www.dtcms.com/a/303254.html

相关文章:

  • B树、B+树、红黑树区别
  • Solidity基础(教程④-ERC-4626收益金库)
  • 被困扰的elementplus样式修改问题:select选择器修改和el-input修改
  • PHP企业级应用架构:微服务通信、分布式事务与性能优化
  • 短剧系统开发上线全流程攻略:从架构设计到性能优化
  • 页面性能优化
  • SpringBoot轻松集成豆包AI
  • Cacti RCE漏洞复现
  • Android Studio关于Connection refused: connect报错
  • “车位到车位”自动驾驶真相
  • pcm,msd调制解调仿真
  • WCF服务通信框架
  • 用友NC漏洞批量检测工具,支持POC显示、单一检测、批量检测、结果导出、AI交互等
  • Parasoft Virtualize用服务虚拟化加速银行系统的软件测试
  • Red Hat OpenShift AI 产品简介
  • XCF32PVOG48C Xilinx Platform Flash PROM
  • 神经网络CNN、RNN、Transform
  • 【实时Linux实战系列】在实时应用中进行负载均衡
  • Docker 部署 Supabase并连接
  • 【Linux】重生之从零开始学习运维之Mysql
  • 深度学习篇---层与层之间搭配
  • 基于Qlearning强化学习的水下无人航行器路径规划与避障系统matlab性能仿真
  • 免费离线翻译软件LibreTranslate免安装一键启动整合包下载
  • JavaScript 回调函数讲解_callback
  • LeetCode|Day28|67. 二进制求和|Python刷题笔记
  • 波形发生器AWG硬件设计方案
  • AW2013 LED驱动芯片 工作方式介绍
  • Java线程池详解:核心参数与工作原理
  • PBR(策略路由)
  • 力扣-39.组合总和