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

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. python中的evalexec 将字符串当做代码执行

    eval/exec 将字符串当做代码执行 eval/exec 这两个函数可以将字符串解析为代码并执行. 区别 1.eval 解析变量和表达式, 而 exec 解析语句 a = '1' print(ev ...

  2. python数据类型之tuple(元组)

    tuple元组 关注公众号"轻松学编程"了解更多. 1.概述 本质上是一种有序的集合,和列表非常的相似,列表使用[]表示,元组使用**()**表示. 特点:一旦初始化,就不能发生改 ...

  3. active cab inf文件编写

    最近做了一个网页下载控件.主要就是实现ActiveX控件功能. 由于自己是第一次做,不熟悉其过程.中间走了很多弯路.现在把走过得路程记录部分,希望对其他人可以有点用. 首先制作一个你自己的DLL文件. ...

  4. P1360 [USACO07MAR]Gold Balanced Lineup G

    \(\mathbf{P1360}\) 题解 思路 设\(sum[t][i]\)为截至第t天第i项能力的提升总次数. 由题意可知一个时期为均衡时期\([t_1,t_2]\),当且仅当 \(\forall ...

  5. CF1008D Pave the Parallelepiped

    容斥原理 解法一: 其他容斥原理的题也可以用这种思想 先把$A$,$B$,$C$分解因数 一种很暴力的想法是,将这些因数分成若干个集合(画出韦恩图),然后对有序数组的三个数分别枚举其位于哪一个集合中 ...

  6. layui表单提交

    关于layui表单提交  只是简单用一个文本框记录一下提交过程    其他的如下拉框选择框样式可以参考官网 下面直接开始.首 一:前台页面 <!DOCTYPE html><html& ...

  7. C语言中利用clock设计一个简单的定时器

    time.h是C/C++中的日期和时间头文件,用于需要时间方面的函数,定义了四个变量类型.两个宏和各种操作日期和时间的函数. 其中计时函数是clock(),而与其相关的数据类型是clock_t.clo ...

  8. charles技能之修改请求参数/返回数据(map Local、Rewrite、Breakpoints)

    之前一直用postman调接口比较多,但有时候想要去修改APP的页面展示,造数据又会比较麻烦,此时可以用以下三种方法修改请求参数或修改响应: map Local(本地映射).Breakpoints(打 ...

  9. PIP安装Django

    1. 2. 3.升级PIP 4. 原来我已经用pycharm装过了,囧~

  10. 设计模式(一)--工厂模式(Go实现)

    package Factory import "fmt" type Restaurant interface { GetFood() } type Donglaishun stru ...