Java--业务场景:SpringBoot 通过Redis进行IP封禁实现接口防刷

文章目录

      • 前言
      • 具体实现步骤
        • 1. 定义自定义注解
        • 2. 编写拦截器类IpUrlLimitInterceptor
        • 3. 在WebConfig类中添加IpUrlLimitInterceptor
        • 4. 添加注解到接口上
      • 测试效果
      • 参考文章

前言

  • 在实际项目中,有些攻击者会使用自动化工具来频繁刷新接口,造成系统的瞬时吞吐量提高,给系统带来很大的压力。要保障服务的安全性,需要防止重要的接口被恶意刷新,接口防刷的方式可以通过设置验证码,IP封禁,安全参数校验等方法。
  • 本文主要采用Redis将同一时间内频繁访问同一接口的IP封禁一段时间的方式来防止接口被恶意刷新。

具体实现步骤

1. 定义自定义注解
  • 添加了该注解的接口,将开启接口防刷功能。
    /**
    * 防刷注解
    */
    @Target(ElementType.METHOD)
    @Documented
    @Retention(RetentionPolicy.RUNTIME)
    public @interface AccessLimit {/*** 表示规定的时间范围*/int seconds();/*** 表示在规定的时间范围内最多可被访问的次数*/int maxCount();/*** 表示该接口是否需要登录,默认为true*/boolean needLogin() default true;
    }
    
2. 编写拦截器类IpUrlLimitInterceptor
  • 核心拦截器IpUrlLimitInterceptor的代码如下:
     @Slf4jpublic class IpUrlLimitInterceptor implements HandlerInterceptor {@AutowiredRedisUtil redisUtil; //redis工具类@Autowiredprivate TokenManager tokenManager; //登录时的token检验管理器private static final String LOCK_IP_URL_KEY = "lock_ip_";private static final String IP_URL_REQ_TIME = "ip_url_times_";private static final int IP_LOCK_TIME = 60; //IP被禁用的时间 此处为了方便测试,设置为一分钟 实际情况应该在配置文件里设置@Overridepublic boolean preHandle(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o) throws Exception {if (o instanceof HandlerMethod) {HandlerMethod hm = (HandlerMethod) o;// 获取AccessLimit注解AccessLimit accessLimit = hm.getMethodAnnotation(AccessLimit.class);if(Objects.isNull(accessLimit)){return true;}log.info("request请求地址uri={},ip={}", httpServletRequest.getRequestURI(), IpUtil.getIp(httpServletRequest));//判断IP是否被锁定,若被锁定则访问异常提示信息if (ipIsLock(IpUtil.getIp(httpServletRequest))) {log.info("ip访问被禁止={}", IpUtil.getIp(httpServletRequest));Result result = Result.exception().code(ResultCode.LOCK_IP).message("该IP已被锁定,请等候解锁");returnJson(httpServletResponse, JSON.toJSONString(result));return false;}//接口若需要登录,则校验token//获取请求头里的token信息判断是否正确,若token不正确,则return falseif(accessLimit.needLogin()&&!tokenManager.checkToken(httpServletRequest.getHeader("Authorization"))){ return false;}//记录请求次数,记录后若大于规定时间内的规定次数则返回异常提示信息if (!addRequestTime(IpUtil.getIp(httpServletRequest), httpServletRequest.getRequestURI(), accessLimit.seconds(),accessLimit.maxCount())) {Result result = Result.exception().code(ResultCode.LOCK_IP).message("该IP已被锁定,请等候解锁");returnJson(httpServletResponse, JSON.toJSONString(result));return false;}}return true;}@Overridepublic void postHandle(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o, ModelAndView modelAndView) throws Exception {}@Overridepublic void afterCompletion(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o, Exception e) throws Exception {}/*** @param ip* @return java.lang.Boolean* @Description: 判断ip是否被禁用*/private Boolean ipIsLock(String ip) {if (redisUtil.hasKey(LOCK_IP_URL_KEY + ip)) {return true;}return false;}/*** @param ip* @param uri* @return java.lang.Boolean* @Description: 记录请求次数*/private Boolean addRequestTime(String ip, String uri,int seconds,int maxCount) {String key = IP_URL_REQ_TIME + ip + uri;if (redisUtil.hasKey(key)) {//访问次数加1long time = redisUtil.incrBy(key, 1);if (time >= maxCount) {redisUtil.getLock(LOCK_IP_URL_KEY + ip, ip, IP_LOCK_TIME);return false;}} else {//seconds秒内访问maxCount次就锁柱redisUtil.getLock(key, 1, seconds);}return true;}private void returnJson(HttpServletResponse response, String json) throws Exception {PrintWriter writer = null;response.setCharacterEncoding("UTF-8");response.setContentType("text/json; charset=utf-8");try {writer = response.getWriter();writer.print(json);} catch (IOException e) {log.error("LoginInterceptor response error ---> {}", e.getMessage(), e);} finally {if (writer != null) {writer.close();}}}}
    
  • 上述代码中的RedisUtil具体方法如下,完整的RedisUtil类获取方式:Java - Redis操作的工具类RedisUtil
    @Component
    @Slf4j
    public class RedisUtil {private static final Long SUCCESS = 1L;@Autowiredprivate RedisTemplate<String, Object> redisTemplate;/*** 获取锁* 代码中redis的使用的是分布式锁的形式,这样可以最大程度保证线程安全和功能的实现效果。* @param lockKey* @param value* @param expireTime:单位-秒* @return*/public boolean getLock(String lockKey, Object value, int expireTime) {try {log.info("添加分布式锁key={},expireTime={}", lockKey, expireTime);String script = "if redis.call('setnx',KEYS[1],ARGV[1]) then if redis.call('get',KEYS[1])==ARGV[1] then return redis.call('expire',KEYS[1],ARGV[2]) else return 0 end end";RedisScript<Long> redisScript = new DefaultRedisScript<>(script, Long.class);Object result = redisTemplate.execute(redisScript, Collections.singletonList(lockKey), value, expireTime);if (SUCCESS.equals(result)) {return true;}} catch (Exception e) {e.printStackTrace();}return false;}//其他方法....
    }
    
  • 拦截器中的IpUtil工具类获取方式:Java-IpUtil通过请求获取IP信息的工具类
3. 在WebConfig类中添加IpUrlLimitInterceptor
  @Configurationpublic class WebConfig extends WebMvcConfigurerAdapter  {@BeanIpUrlLimitInterceptor getIpUrlLimitInterceptor() {return new IpUrlLimitInterceptor();}/*** 注册登录ip防刷拦截器* @return*/@Overridepublic void addInterceptors(InterceptorRegistry registry) {registry.addInterceptor(getIpUrlLimitInterceptor()).addPathPatterns("/**");super.addInterceptors(registry);}
}
4. 添加注解到接口上
  • 编写一个接口,将刚刚的防刷注解添加上去
  @RestController@RequestMapping("/part/util")public class UtilController {/*** 防刷注解测试* @return*/@GetMapping("/ipLimitTest")@AccessLimit(seconds = 1,maxCount = 5,needLogin = false)//表示一秒内该接口只能访问五次,防止恶意刷流量,这里接口无需登录public Result ipLimitTest(){return Result.ok().data("访问成功");} }

测试效果

  • 手写一个for循环请求10次ipLimitTest()接口,观察日志情况如下:
    在这里插入图片描述

  • 超过五次之后,该ip就被锁定1分钟。一分钟内的访问被禁止。此时查询redis的key,可以发现该ip锁住。
    在这里插入图片描述

参考文章

如何解决SpringBoot 接口恶意刷新和暴力请求?(荣耀典藏版)

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/618560.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

单因素方差分析--R

任务说明 三个剂量水平的药物处理受试者&#xff0c;每个剂量水平十个受试者&#xff0c;现在收集到数据后&#xff0c;问&#xff1a; 药物剂量水平显著影响受试者的response&#xff1f; 或者不同剂量药物处理受试者有显著效果的差异吗&#xff1f; 数据 library(tidyvers…

rk3568下SoftBusDumpDeviceInfo执行错误—鸿蒙开发已解决

文章目录 项目场景:问题描述原因分析:解决方案:此Bug解决方案总结寄语项目场景: 最近也是遇到了这个问题,看到网上也有人在询问这个问题,本文总结了自己和其他人的解决经验,解决了rk3568下SoftBusDumpDeviceInfo执行错误的问题。 命令行运行 SoftBusDumpDeviceInfo,测…

技术解密:如何巧妙设计层级结构,轻松解决中间节点删除的挑战?

嗨&#xff0c;大家好&#xff0c;我是小米&#xff01;今天我们要聊的话题是一个非常有趣而且挑战性的面试题&#xff1a;“数据是层级结构的&#xff0c;怎么设计方便查询和修改&#xff1f;如果删除中间一个节点&#xff0c;如何保证不改大量数据&#xff1f;”废话不多说&a…

css选择器在python中如何使用

css选择器整理&#xff1a;https://blog.csdn.net/qq_40910788/article/details/84842951 目标&#xff1a;爬取某文章网站列表&#xff1a; 基础代码如下&#xff1a; import random import time import urllib.request import redef reptileTest(url):try:my_headers [&q…

人工智能技术的应用

人工智能技术的应用&#xff1a;无人手机可以通过集成人工智能技术&#xff0c;实现语音识别、语音合成、自然语言处理等功能&#xff0c;从而实现更加智能化的交互体验。 室内定位技术的改进&#xff1a;无人手机需要准确地了解自己的位置信息&#xff0c;以便更好地为用户提…

Self-Attention

前置知识&#xff1a;RNN&#xff0c;Attention机制 在一般任务的Encoder-Decoder框架中&#xff0c;输入Source和输出Target内容是不一样的&#xff0c;比如对于英-中机器翻译来说&#xff0c;Source是英文句子&#xff0c;Target是对应的翻译出的中文句子&#xff0c;Attent…

Mybatis-Plus常用注解总结

Mybatis-Plus是一个在Mybatis基础上进行增强的ORM框架&#xff0c;提供了许多便捷的注解来简化数据库操作。本文将介绍Mybatis-Plus常用的注解以及它们的使用方法。 1. TableName TableName注解用于指定实体类对应的数据库表名。使用该注解可以省去手动编写SQL语句的繁琐过程…

深入理解虚拟DOM:原理、优势与实践

文章目录 1. DOM操作与性能问题1 DOM操作对性能的影响&#xff1a;重绘与重排2 频繁DOM操作导致的问题3 案例分析&#xff1a;传统Web应用中的性能瓶颈 2. 虚拟DOM的概念与原理1 虚拟DOM的定义2 虚拟DOM与真实DOM的关系虚拟DOM的工作原理&#xff1a;初始化、更新、对比、渲染 …

编程笔记 html5cssjs 037 CSS选择器

编程笔记 html5&css&js 037 CSS选择器 一、CSS 选择器二、CSS 元素选择器三、CSS id 选择器四、CSS 类选择器五、CSS 通用选择器小结 CSS用于处理网页的样式&#xff0c;就像一个人的装扮&#xff0c;拿来一个衣物或饰品&#xff0c;你得知道穿着在什么部位&#xff0c…

Danswer部署指南

Quickstart How to deploy Danswer on your local machine ​ Requirements gitdocker with compose (docker version > 1.13.0) ​ Setup This quickstart guide covers setting up Danswer for local execution Clone the Danswer repo: git clone https://github.com…

Mysql 数据库ERROR 1820 (HY000): You must reset your password using ALTER USER 解决办法

Mysql 5.7数据库原来一直都能正常访问&#xff0c;突然访问不了&#xff0c;查看日志提示数据库需要修改密码&#xff0c; 具体解决办法如下操作&#xff1a; Windows 下&#xff1a; mysql的bin目录下&#xff0c; mysql>use mysql; mysql>mysql -uroot -p密码; 判…

gem5学习(14):将gem5扩展到ARM——Extending gem5 for ARM

目录 一、Downloading ARM Binaries 二、Building gem5 to run ARM Binaries 三、Modifying simple.py to run ARM Binaries 四、Running gem5 五、ARM Full System Simulation An aside on FS simulations 这个是gem5-learning中Getting Started的最后一篇文章&#xff…

信创平台迁移认知误区

误区一 问题&#xff1a;应用采用JDK1.5、JDK1.6进行开发&#xff0c;是否可以迁移到信创平台的TongWeb下&#xff0c;需要用TongWeb哪个版本 &#xff1f; 错误答复&#xff1a;JDK1.5需要采用TongWeb5.0、JDK1.6需要采用TongWeb6.1、JDK1.7需要采用TongWeb7.0、最新TongWe…

imgaug库指南(20):从入门到精通的【图像增强】之旅

引言 在深度学习和计算机视觉的世界里&#xff0c;数据是模型训练的基石&#xff0c;其质量与数量直接影响着模型的性能。然而&#xff0c;获取大量高质量的标注数据往往需要耗费大量的时间和资源。正因如此&#xff0c;数据增强技术应运而生&#xff0c;成为了解决这一问题的…

c# 文本加密解密

在C#中实现加密功能&#xff0c;您可以使用.NET框架提供的System.Security.Cryptography命名空间下的多种加密算法类。这里我将给出一个使用AES&#xff08;Advanced Encryption Standard&#xff09;对称加密算法的简单示例&#xff1a; using System; using System.IO; usin…

Android Jetpack中Lifecycle使用生命周期感知型组件处理生命周期

在使用Kotlin实现Android Jetpack中Lifecycle使用生命周期感知型组件处理生命周期的功能时&#xff0c;你需要以下步骤&#xff1a; 首先&#xff0c;在app的build.gradle文件中添加Lifecycle相关库的依赖项&#xff1a; implementation "androidx.lifecycle:lifecycle-…

各版本 操作系统 对 .NET Framework 与 .NET Core 支持

有两种类型的受支持版本&#xff1a;长期支持 (LTS) 版本和标准期限支持 (STS) 版本。 所有版本的质量都是一样的。 唯一的区别是支持的时间长短。 LTS 版本可获得为期三年的免费支持和补丁。 STS 版本可获得 18 个月的免费支持和修补程序。 有关详细信息&#xff0c;请参阅 .N…

Java重修第五天—面向对象2

通过学习本篇文章可以掌握如下知识 static&#xff1b;设计单例&#xff1b;继承。 之前文章我们已经对面向对象进行了入门学习&#xff0c;这篇文章我们就开始深入了解面向对象设计。 static 我们定义了一个 Student类&#xff0c;增加姓名属性&#xff1a;name &#xff1…

[paddle]paddlehub部署paddleocr的hubserving服务

步骤如下&#xff1a; 第一步&#xff1a;首先需要安装好paddleocr环境已经paddlehub环境 第二步&#xff1a;下载paddleocr源码&#xff1a; git clone https://github.com/PaddlePaddle/PaddleOCR.git 然后切换到paddocr目录执行 新建个文件夹叫Inference把paddleocr模型…

用通俗易懂的方式讲解:内容讲解+代码案例,轻松掌握大模型应用框架 LangChain

本文介绍了 LangChain 框架&#xff0c;它能够将大型语言模型与其他计算或知识来源相结合&#xff0c;从而实现功能更加强大的应用。 接着&#xff0c;对LangChain的关键概念进行了详细说明&#xff0c;并基于该框架进行了一些案例尝试&#xff0c;旨在帮助读者更轻松地理解 L…