当前位置:   article > 正文

springboot接口服务,防刷、防止请求攻击,AOP实现_springboot验证码防刷

springboot验证码防刷

本文使用AOP的方式防止spring boot的接口服务被网络攻击

  1. pom.xml 中加入 AOP 依赖

  1. <dependency>
  2. <groupId>org.springframework.boot</groupId>
  3. <artifactId>spring-boot-starter-aop</artifactId>
  4. </dependency>
  1. AOP自定义注解类

  1. package org.jeecg.common.aspect.annotation;
  2. import java.lang.annotation.*;
  3. /**
  4. * 用于防刷限流的注解
  5. * 默认是5秒内只能调用一次
  6. */
  7. @Target({ ElementType.METHOD })
  8. @Retention(RetentionPolicy.RUNTIME)
  9. @Documented
  10. public @interface RateLimit {
  11. /** 限流的key */
  12. String key() default "limit:";
  13. /** 周期,单位是秒 */
  14. int cycle() default 5;
  15. /** 请求次数 */
  16. int count() default 1;
  17. /** 默认提示信息 */
  18. String msg() default "请勿重复点击";
  19. }
  1. AOP切面业务类

  1. package org.jeecg.common.aspect;
  2. import org.aspectj.lang.ProceedingJoinPoint;
  3. import org.aspectj.lang.annotation.Around;
  4. import org.aspectj.lang.annotation.Aspect;
  5. import org.aspectj.lang.annotation.Pointcut;
  6. import org.aspectj.lang.reflect.MethodSignature;
  7. import org.jeecg.common.aspect.annotation.RateLimit;
  8. import org.springframework.aop.aspectj.MethodInvocationProceedingJoinPoint;
  9. import org.springframework.data.redis.core.RedisTemplate;
  10. import org.springframework.stereotype.Component;
  11. import org.springframework.web.context.request.RequestContextHolder;
  12. import org.springframework.web.context.request.ServletRequestAttributes;
  13. import javax.annotation.Resource;
  14. import javax.servlet.http.HttpServletRequest;
  15. import java.lang.reflect.Method;
  16. import java.util.concurrent.TimeUnit;
  17. /**
  18. * 切面类:实现限流校验
  19. */
  20. @Aspect
  21. @Component
  22. public class AccessLimitAspect {
  23. @Resource
  24. private RedisTemplate<String, Integer> redisTemplate;
  25. /**
  26. * 这里我们使用注解的形式
  27. * 当然,我们也可以通过切点表达式直接指定需要拦截的package,需要拦截的class 以及 method
  28. */
  29. @Pointcut("@annotation(org.jeecg.common.aspect.annotation.RateLimit)")
  30. public void limitPointCut() {
  31. }
  32. /**
  33. * 环绕通知
  34. */
  35. @Around("limitPointCut()")
  36. public Object around(ProceedingJoinPoint pjp) throws Throwable {
  37. // 获取被注解的方法
  38. MethodInvocationProceedingJoinPoint mjp = (MethodInvocationProceedingJoinPoint) pjp;
  39. MethodSignature signature = (MethodSignature) mjp.getSignature();
  40. Method method = signature.getMethod();
  41. // 获取方法上的注解
  42. RateLimit rateLimit = method.getAnnotation(RateLimit.class);
  43. if (rateLimit == null) {
  44. // 如果没有注解,则继续调用,不做任何处理
  45. return pjp.proceed();
  46. }
  47. /**
  48. * 代码走到这里,说明有 RateLimit 注解,那么就需要做限流校验了
  49. * 1、这里可以使用Redis的API做计数校验
  50. * 2、这里也可以使用Lua脚本做计数校验,都可以
  51. */
  52. //获取request对象
  53. ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
  54. HttpServletRequest request = attributes.getRequest();
  55. // 获取请求IP地址
  56. String ip = getIpAddr(request);
  57. // 请求url路径
  58. String uri = request.getRequestURI();
  59. //存到redis中的key
  60. String key = "RateLimit:" + ip + ":" + uri;
  61. // 缓存中存在key,在限定访问周期内已经调用过当前接口
  62. if (redisTemplate.hasKey(key)) {
  63. // 访问次数自增1
  64. redisTemplate.opsForValue().increment(key, 1);
  65. // 超出访问次数限制
  66. if (redisTemplate.opsForValue().get(key) > rateLimit.count()) {
  67. throw new RuntimeException(rateLimit.msg());
  68. }
  69. // 未超出访问次数限制,不进行任何操作,返回true
  70. } else {
  71. // 第一次设置数据,过期时间为注解确定的访问周期
  72. redisTemplate.opsForValue().set(key, 1, rateLimit.cycle(), TimeUnit.SECONDS);
  73. }
  74. return pjp.proceed();
  75. }
  76. //获取请求的归属IP地址
  77. private String getIpAddr(HttpServletRequest request) {
  78. String ipAddress = null;
  79. try {
  80. ipAddress = request.getHeader("x-forwarded-for");
  81. if (ipAddress == null || ipAddress.length() == 0 || "unknown".equalsIgnoreCase(ipAddress)) {
  82. ipAddress = request.getHeader("Proxy-Client-IP");
  83. }
  84. if (ipAddress == null || ipAddress.length() == 0 || "unknown".equalsIgnoreCase(ipAddress)) {
  85. ipAddress = request.getHeader("WL-Proxy-Client-IP");
  86. }
  87. if (ipAddress == null || ipAddress.length() == 0 || "unknown".equalsIgnoreCase(ipAddress)) {
  88. ipAddress = request.getRemoteAddr();
  89. }
  90. // 对于通过多个代理的情况,第一个IP为客户端真实IP,多个IP按照','分割
  91. if (ipAddress != null && ipAddress.length() > 15) {
  92. // = 15
  93. if (ipAddress.indexOf(",") > 0) {
  94. ipAddress = ipAddress.substring(0, ipAddress.indexOf(","));
  95. }
  96. }
  97. } catch (Exception e) {
  98. ipAddress = "";
  99. }
  100. return ipAddress;
  101. }
  102. }
  1. 测试

  1. package org.jeecg.modules.api.controller;
  2. import org.jeecg.common.api.vo.Result;
  3. import org.jeecg.common.aspect.annotation.RateLimit;
  4. import org.springframework.web.bind.annotation.GetMapping;
  5. import org.springframework.web.bind.annotation.RequestMapping;
  6. import org.springframework.web.bind.annotation.RestController;
  7. /**
  8. * 测试接口
  9. * @author wujiangbo
  10. * @date 2022-08-23 18:50
  11. */
  12. @RestController
  13. @RequestMapping("/test")
  14. public class TestController {
  15. //4秒内只能访问2次
  16. @RateLimit(key= "testLimit", count = 2, cycle = 4, msg = "大哥、慢点刷请求!")
  17. @GetMapping("/test001")
  18. public Result<?> rate() {
  19. System.out.println("请求成功");
  20. return Result.OK("请求成功!");
  21. }
  22. }

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/花生_TL007/article/detail/503422
推荐阅读
相关标签
  

闽ICP备14008679号