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

SpringBoot+redis实现接口防刷

  1. 写一个RedisService,实现获取Redis 的set、get、incr(相当于计数器)

  2. 写@inferface注解类

  3. 做一个拦截器,因为要先于控制器判断

  4. 将拦截器注入Springboot

文章目录

目录

文章目录

前言

一、引入依赖

二、使用步骤

2.1 RedisService操作redis

2.2 防刷的自定义注解

2.3 自定义的过滤器

2.4 测试的控制类 

2.5 测试结果 

总结



前言


一、引入依赖

<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency><dependency><groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>1.2.78</version>
</dependency>

application.properties

spring.redis.port=6379
spring.redis.host=127.0.0.1
spring.redis.password=123456

二、使用步骤

2.1 RedisService操作redis

@Service
public class RedisService {private final StringRedisTemplate stringRedisTemplate;@Autowiredpublic RedisService(StringRedisTemplate stringRedisTemplate) {this.stringRedisTemplate = stringRedisTemplate;}public void set(String key, String value) {ValueOperations<String, String> operations = stringRedisTemplate.opsForValue();operations.set(key, value, 1, TimeUnit.MINUTES); // 设置值时设置过期时间为1分钟}public void set(String key, String value,int N) {ValueOperations<String, String> operations = stringRedisTemplate.opsForValue();operations.set(key, value, N, TimeUnit.MINUTES); // 设置值时设置过期时间为N分钟}public String get(String key) {ValueOperations<String, String> operations = stringRedisTemplate.opsForValue();return operations.get(key);}public Long incr(String key, long delta) {return performIncrement(key, delta);}public Long incr(String key) {return performIncrement(key, 1); // 使用默认增量 1}private Long performIncrement(String key, long delta) {ValueOperations<String, String> operations = stringRedisTemplate.opsForValue();// 检查键是否存在,如果不存在,则设置初始值为 0 并设置过期时间if (Boolean.FALSE.equals(stringRedisTemplate.hasKey(key))) {operations.set(key, "0", 1, TimeUnit.MINUTES); // 设置过期时间为 1 分钟}return operations.increment(key, delta);}
}

2.2 防刷的自定义注解

@Retention(RUNTIME)
@Target(METHOD)
public @interface AccessLimit {//每分钟int minutes();//访问的次数限制int maxCount();
//是否需要登录boolean needLogin()default true;
}

 

2.3 自定义的过滤器

过滤器主要是访问的时候 处理加了防刷注解的接口 然后限制访问给出不一样的返回结果

@Component
public class PreventRefreshInterceptor extends HandlerInterceptorAdapter {@Autowiredprivate RedisService redisService;@Overridepublic boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {System.out.println("进入拦截器");//判断请求是否属于方法的请求if(handler instanceof HandlerMethod){System.out.println("判断请求是否属于方法的请求");HandlerMethod hm = (HandlerMethod) handler;//获取方法中的注解,看是否有该注解AccessLimit accessLimit = hm.getMethodAnnotation(AccessLimit.class);if(accessLimit == null){return true;}//@AccessLimit(seconds=5, maxCount=5, needLogin=true)int minutes = accessLimit.minutes();int maxCount = accessLimit.maxCount();boolean login = accessLimit.needLogin();Map<String, Object> mp=new HashMap<>();mp.put("seconds",minutes);mp.put("maxCount",maxCount);mp.put("login",login);System.out.println(mp);String key = IPUtil.getIpAddr(request)+"_"+request.getRequestURI();log.error("Key:{}",key);//如果需要登录
//            if(login){
//                redisService.set(key, "1");
//            }//从redis中获取用户访问的次数String value = redisService.get(key);System.out.println("value:"+value);//判断value是否为nullif(value == null){System.out.println("第1次访问,设置有效期"+minutes +"分钟");redisService.set(key, "1",minutes);}else{Long count = redisService.incr(key);System.out.println("第"+count+"次访问,设置有效期"+minutes +"分钟");System.out.println("{count :"+count + ", maxCount :"+maxCount+"}");if(count.intValue() > maxCount){System.out.println("超出访问次数");redisService.set(key, "超出次数");renderError(response);}}}return true;}private void renderError(HttpServletResponse response)throws Exception {response.setContentType("application/json;charset=UTF-8");OutputStream out = response.getOutputStream();String str  = JSON.toJSONString(ResultT.error("error","超出访问次数"));out.write(str.getBytes("UTF-8"));out.flush();out.close();}
}

把修改后的过滤器给spring mvc的web过滤器

@Configuration
public class WebConfig extends WebMvcConfigurerAdapter {@Autowiredprivate PreventRefreshInterceptor interceptor;@Overridepublic void addInterceptors(InterceptorRegistry registry) {System.out.println("进入拦截器配置类");registry.addInterceptor(interceptor);}
}

2.4 测试的控制类 

@RestController
public class RefreshController {@AccessLimit(minutes=2, maxCount=10, needLogin=true)@RequestMapping("/shua")@ResponseBodypublic ResultT shua(){return ResultT.ok("请求成功");}}

统一结果类

public class ResultT extends HashMap<String, Object> {public ResultT() {put("code", 0);put("msg", "success");}public static ResultT ok() {ResultT t = new ResultT();t.put("msg", "操作成功");return t;}public static ResultT ok(String msg) {ResultT t = new ResultT();t.put("msg", msg);return t;}public static ResultT ok(Map<String, Object> map) {ResultT t = new ResultT();map.putAll(t);return t;}public static ResultT error(String code,String msg) {ResultT t = new ResultT();t.put("code", code);t.put("msg", msg);return t;}public static ResultT error(String msg) {return error("500", msg);}public ResultT put(String key, Object value){super.put(key, value);return this;}
}public class IPUtil {private static final String UNKNOWN = "unknown";protected IPUtil() {}/*** 获取 IP地址* 使用 Nginx等反向代理软件, 则不能通过 request.getRemoteAddr()获取 IP地址* 如果使用了多级反向代理的话,X-Forwarded-For的值并不止一个,而是一串IP地址,* X-Forwarded-For中第一个非 unknown的有效IP字符串,则为真实IP地址*/public static String getIpAddr(HttpServletRequest request) {String ip = request.getHeader("x-forwarded-for");if (ip == null || ip.length() == 0 || UNKNOWN.equalsIgnoreCase(ip)) {ip = request.getHeader("Proxy-Client-IP");}if (ip == null || ip.length() == 0 || UNKNOWN.equalsIgnoreCase(ip)) {ip = request.getHeader("WL-Proxy-Client-IP");}if (ip == null || ip.length() == 0 || UNKNOWN.equalsIgnoreCase(ip)) {ip = request.getRemoteAddr();}return "0:0:0:0:0:0:0:1".equals(ip) ? "127.0.0.1" : ip;}}

2.5 测试结果 

 


总结

http://www.lryc.cn/news/250761.html

相关文章:

  • 5G承载网和大客户承载的演进
  • 智慧工地一体化解决方案(里程碑管理)源码
  • 熬夜会秃头——beta冲刺Day2
  • 【linux】信号——信号保存+信号处理
  • 雷军:我的程序人生路
  • Linux 磁盘分区处理
  • 利用ogr2ogr从PostGIS中导出/导入Tab/Dxf/Geojson等格式数据
  • 【深度优先】LeetCode1932:合并多棵二叉搜索树
  • monorepo多项目管理主流实现方式:1.learn + yarn/npm workspace 2.pnpm
  • 【斗罗二】暗杀霍雨浩行动,马小桃霸气回击,江楠楠首秀武魂兔兔
  • [ 蓝桥杯Web真题 ]-年度明星项目
  • Maven终端打包时报Unknown lifecycle phase “.test.skip=true“
  • Linux MIPI 调试中常见的问题
  • 使用极限网关助力 ES 集群无缝升级、迁移上/下云
  • RedisTemplate的配置和讲解以及和StringRedisTemplate的区别
  • 在oracle中的scn技术
  • LINUX 嵌入式C编程--信号编程
  • Linux:优化原则
  • HarmonyOs 4 (一) 认识HarmonyOs
  • System.out.println隐藏字符串
  • Java中的线程池你了解多少?
  • leetCode 131.分割回文串 + 动态规划 + 回溯算法 + 优化 + 图解 + 笔记
  • 【傻瓜级JS-DLL-WINCC-PLC交互】3.JS-DLL进行交互
  • 深度学习手势识别算法实现 - opencv python 计算机竞赛
  • 2023-12-01 AIGC-自动生成ppt的AI工具
  • NoSQL 数据建模错误会降低性能
  • 在Android上搭建一个NDK项目
  • TOP-K问题和向上调整算法和向下调整算法的时间复杂度问题的分析
  • 3、服务器性能剖析
  • xxl-job 分布式任务调度框架