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

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创建Django项目

    创建Django项目 关注公众号"轻松学编程"了解更多. 创建一个HelloDjango项目 GitHub地址:https://github.com/liangdongchang/ ...

  2. [BZOJ 4818/LuoguP3702][SDOI2017] 序列计数 (矩阵加速DP)

    题面: 传送门:https://www.lydsy.com/JudgeOnline/problem.php?id=4818 Solution 看到这道题,我们不妨先考虑一下20分怎么搞 想到暴力,本蒟 ...

  3. git 的一些常用命令

    1. git clone **(项目地址) 克隆一个git项目到本地,将git项目拉取到本地 2. git status 查看文件状态,列出当前目录没有被git管理,以及被修改过还未提交的文件 3. ...

  4. AI时代,还不了解大数据?

    如果要问最近几年,IT行业哪个技术方向最火?一定属于ABC,即AI + Big Data + Cloud,也就是人工智能.大数据和云计算. 这几年,随着互联网大潮走向低谷,同时传统企业纷纷进行数字化转 ...

  5. http代理阅读3 发送mem处理

    每次客户端有可读数据触发时,优先检测是否还有数据没有发送,如果有则发送数据,然后在读取client数据 //向后端发送请求的调用过程 //ngx_http_upstream_send_request_ ...

  6. Selective Acknowledgment 选项 浅析 1

     抓包的时候,发现 tcp 三次握手中一般会有几个options  一个是mss 一个是ws  一个sack perm 这次主要是来说一说 sack 这个选项: 1. 只重传超时的数据包,比较实用与后 ...

  7. Pytest学习(十) - parametrize、fixture、request的混合使用

    一.前言 上篇文章有提及pytest.mark.parametrize的使用,这次在此基础上结合fixture和request再做个延伸. 二.传单个参数 即一个参数一个值,示例代码如下: # 传单个 ...

  8. RestPack Java实现Html转PDF文件

    最近公司需要将前端一个图表统计导出为pdf.前端导出显示的pdf还是可以的,但是将会导致页面不可用与卡死状态.所以由后端寻找解决方案. 以下为解决方案调研 https://www.cnblogs.co ...

  9. 查看 /var/log目录下文件个数 命令tree 、cut

    查看 /var/log目录下文件个数 方法1. [root@oldboy learn_shell]# tree -L 1 /var/log/ |tail -1 5 directories, 42 fi ...

  10. IGH_Master主站配置驱动伺服电机和变频器总结

    IGH_Master主站配置驱动伺服电机和变频器总结 Ethercat是倍福公司提出的一种工业现场总线协议,具有很好的实时性,IGH是一种开源的Ethercat主站实现协议,本文总结了一下使用IGH_ ...