一、问题:项目中有一些重复操作的情况,比如:

1.从场景有用户快速点击提交按钮,或者postMan测试时快速点击

2.从业务上来说,用户注册、用户下单等

3.黑客攻击

二、解决办法

1、使用springAop、Redis

2、代码

/**
* 2020/7/22 9:59 AM
*
* @author shoo
* @describe 校验重复操作 (aop实现)
*/
@Aspect
@Component
public class ParaLogAspect { private static final Logger logger = LoggerFactory.getLogger(ParaLogAspect.class); @Autowired
private RedisTemplate<String, Object> redisTemplate; //定义一个切点,要切的类、方法
@Pointcut("execution(* com.meritdata.cloud.middleplatform.dataservice.account.integral.controller.*.*(..))" +
"||execution(* com.meritdata.cloud.middleplatform.dataservice.account.platformBase.controller.*.*(..))" +
"||execution(* com.meritdata.cloud.middleplatform.dataservice.account.storeConsume.controller.*.*(..))" +
"||execution(* com.meritdata.cloud.middleplatform.dataservice.account.vipcard.controller.*.*(..))" +
"||execution(* com.meritdata.cloud.middleplatform.dataservice.shell.controller.*.*(..))")
public void authRepeat(){ } @Around("authRepeat()")
public Object authRepeat(ProceedingJoinPoint joinPoint) throws Throwable{ ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
HttpServletRequest request = attributes.getRequest();
StringBuffer body = new StringBuffer();
Object[] arguments = joinPoint.getArgs();
//获取方法的参数
//注意这里只取了第一个参数,如果想兼容多个参数的方法请自行处理
if(arguments.length!=0){
try {
Map<String, Object> params = params = (Map<String, Object>)arguments[0];
for (String key:params.keySet()
) {
body.append(key).append("=").append(params.get(key)).append("&");
} }catch (Exception ex){
logger.info("=====方法接收参数[{}]",arguments[0].toString());
}
}
// key:请求者IP+请求URL+参数
String key = request.getRemoteAddr() + ";" + request.getRequestURL().toString() + "?" + body.toString();
logger.info("====key[{}]",key.substring(0,key.length()-1)); Object obj = null;
Object[] args = joinPoint.getArgs();
//重复提交校验
if(!authRepeat(key)){
logger.info("重复提交,key[{}]",key);
return MapResult.build("请勿频繁操作",false);
}
//不是重复提交则继续主进程
try {
obj = joinPoint.proceed(args);
} catch (Throwable e) {
logger.error("重复操作校验环绕通知出错", e);
}
return obj;
} //重复提交校验
// Redis的increment方法:把key的值加上指定数值,如果key不存在则默认创建,该操作是单线程的
private boolean authRepeat(String key){ long repeat = redisTemplate.opsForValue().increment(key,1);
logger.info("repeat:[{}]",repeat);
if(repeat>1){
return false;
}
redisTemplate.expire(key,1, TimeUnit.SECONDS);
return true;
}
}

3.说明

a、首先用springAop切入需要校验的类或者方法,这里用的是环绕通知(around),如果一秒内操作次数超过一次则返回错误提示请勿频繁操作

b、校验规则是 请求者IP+请求URL+方法参数

c、Redis的increment是单线程的原子操作

三、测试

用locust启动十个用户同时访问用户注册接口,数据库中只注册成功了一个数据,剩余的都提示请勿频繁操作

spring aop 、Redis实现拦截重复操作的更多相关文章

  1. 今日份学习:写一些代码 (Spring+AOP+Redis+MySQL练习)

    笔记 Spring+AOP+Redis+MySQL练习 1. 启动docker->mysql docker run --name mysql -v e:\docker:/var/lib/mysq ...

  2. TinyFrame再续篇:整合Spring AOP实现日志拦截

    上一篇中主要讲解了如何使用Spring IOC实现依赖注入的.但是操作的时候,有个很明显的问题没有解决,就是日志记录问题.如果手动添加,上百个上千个操作,每个操作都要写一遍WriteLog方法,工作量 ...

  3. Spring AOP原理及拦截器

    原理 AOP(Aspect Oriented Programming),也就是面向方面编程的技术.AOP基于IoC基础,是对OOP的有益补充. AOP将应用系统分为两部分,核心业务逻辑(Core bu ...

  4. Spring aop 实现异常拦截

    使用aop异常挂载功能可以统一处理方法抛出的异常,减少很多重复代码,实现如下: 1.实现ThrowAdvice public class ExceptionHandler implements Thr ...

  5. 从零开始学 Java - Spring AOP 拦截器的基本实现

    一个程序猿在梦中解决的 Bug 没有人是不做梦的,在所有梦的排行中,白日梦最令人伤感.不知道身为程序猿的大家,有没有睡了一觉,然后在梦中把睡之前代码中怎么也搞不定的 Bug 给解决的经历?反正我是有过 ...

  6. Java - Spring AOP 拦截器的基本实现

    一个程序猿在梦中解决的 Bug 没有人是不做梦的,在所有梦的排行中,白日梦最令人伤感.不知道身为程序猿的大家,有没有睡了一觉,然后在梦中把睡之前代码中怎么也搞不定的 Bug 给解决的经历?反正我是有过 ...

  7. Spring学习总结(15)——Spring AOP 拦截器的基本实现

    一个程序猿在梦中解决的 Bug 没有人是不做梦的,在所有梦的排行中,白日梦最令人伤感.不知道身为程序猿的大家,有没有睡了一觉,然后在梦中把睡之前代码中怎么也搞不定的 Bug 给解决的经历?反正我是有过 ...

  8. Spring AOP无法拦截内部方法调用

    当在同一个类中,A方法调用B方法时,AOP无法工作的问题 假设一个接口里面有两个方法: package demo.long; public interface CustomerService { pu ...

  9. 【Spring AOP】AOP介绍(一)

    AOP(Aspect Oriented Programming) 面向切面编程,是Spring框架的一个重要组件. AOP应该算是对OOP(面向对象编程)的补充和完善.OOP引入封装.继承.多态等概念 ...

随机推荐

  1. LWJGL3的内存管理,第一篇,基础知识

    LWJGL3的内存管理,第一篇,基础知识 为了讨论LWJGL在内存分配方面的设计,我将会分为数篇随笔分开介绍,本篇将主要介绍一些大方向的问题和一些必备的知识. 何为"绑定(binding)& ...

  2. Linux的进程、线程、文件描述符是什么

    说到进程,恐怕面试中最常见的问题就是线程和进程的关系了,那么先说一下答案:在 Linux 系统中,进程和线程几乎没有区别. Linux 中的进程就是一个数据结构,看明白就可以理解文件描述符.重定向.管 ...

  3. 关于H5页面在微信浏览器中音视频播放的问题

    Android 上,因为各个软件使用的浏览器渲染引擎不一样,所以视频播放的效果差异也很大,这里主要以微信为主.微信使用的是腾讯浏览器自带的X5内核. 而iOS是不允许使用第三方浏览器内核的,就是Goo ...

  4. 自制 os 极简教程1:写一个操作系统有多难

    为什么叫极简教程呢?听我慢慢说 不知道正在阅读本文的你,是否是因为想自己动手写一个操作系统.我觉得可能每个程序员都有个操作系统梦,或许是想亲自动手写出来一个,或许是想彻底吃透操作系统的知识.不论是为了 ...

  5. Blazor中的CSS隔离

    1.环境 VS 2019 16.9.0 Preview 1.0 .NET SDK 5.0.100 2.前言 CSS一旦生效,就会应用于全局,所以很容易出现冲突.为了解决这个问题CSS隔离就顺势而生.B ...

  6. MiniCat:手写Http服务器

    minicat 项目介绍 已实现http基础协议.参数接受.servlet.filter.cookie.多文件上传等.支持NIO. 一款轻量化Http服务器.支持bio.nio两种模式.归属Coody ...

  7. 手写一个最迷你的Web服务器

    今天我们就仿照Tomcat服务器来手写一个最简单最迷你版的web服务器,仅供学习交流. 1. 在你windows系统盘的F盘下,创建一个文件夹webroot,用来存放前端代码.  2. 代码介绍: ( ...

  8. 高性能arm运行ceph存储基准测试

    关于arm 之前wdlab对外发布过一次约500个节点的arm的ceph集群,那个采用的是微集群的结构,使用的是双核的cortex-a9 ARM处理器,运行速度为1.3 GHz,内存为1 GB,直接焊 ...

  9. centos6安装calamari

    安装操作系统 首先安装操作系统centos6,安装过程选择的是base server,这个不相同不要紧,出现缺少包的时候去iso找出来安装就可以了 calamari的简单介绍 首先简单的介绍下cala ...

  10. Python_列表相减(判断长度后长的减短的)

    #定义一个方法,可进行列表相减 class V(object): def __init__(self,*value): self.value=value def __sub__(self,other) ...