⭐⭐⭐ Spring Boot 项目实战 ⭐⭐⭐ Spring Cloud 项目实战
《Dubbo 实现原理与源码解析 —— 精品合集》 《Netty 实现原理与源码解析 —— 精品合集》
《Spring 实现原理与源码解析 —— 精品合集》 《MyBatis 实现原理与源码解析 —— 精品合集》
《Spring MVC 实现原理与源码解析 —— 精品合集》 《数据库实体设计合集》
《Spring Boot 实现原理与源码解析 —— 精品合集》 《Java 面试题 + Java 学习指南》

摘要: 原创出处 blog.csdn.net/wang_shuyu/article/details/102531940/ 「wang_shuyu」欢迎转载,保留摘要,谢谢!


🙂🙂🙂关注**微信公众号:【芋道源码】**有福利:

  1. RocketMQ / MyCAT / Sharding-JDBC 所有源码分析文章列表
  2. RocketMQ / MyCAT / Sharding-JDBC 中文注释源码 GitHub 地址
  3. 您对于源码的疑问每条留言将得到认真回复。甚至不知道如何读源码也可以请教噢
  4. 新的源码解析文章实时收到通知。每周更新一篇左右
  5. 认真的源码交流微信群。

在实际项目使用中,必须要考虑服务的安全性,当服务部署到互联网以后,就要考虑服务被恶意请求和暴力攻击的情况,下面的教程,通过interceptredis针对url+ip在一定时间内访问的次数来将ip禁用,可以根据自己的需求进行相应的修改,来打打自己的目的;

首先工程为springboot框架搭建,不再详细叙述。

直接上核心代码。

首先创建一个自定义的拦截器类,也是最核心的代码:

/**
* @package: com.technicalinterest.group.interceptor
* @className: IpUrlLimitInterceptor
* @description: ip+url重复请求现在拦截器
* @author: Shuyu.Wang
* @since: 0.1
**/
@Slf4j
public class IpUrlLimitInterceptor implements HandlerInterceptor {


private RedisUtil getRedisUtil() {
return SpringContextUtil.getBean(RedisUtil.class);
}

private static final String LOCK_IP_URL_KEY="lock_ip_";

private static final String IP_URL_REQ_TIME="ip_url_times_";

private static final long LIMIT_TIMES=5;

private static final int IP_LOCK_TIME=60;

@Override
public boolean preHandle(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o) throws Exception {
log.info("request请求地址uri={},ip={}", httpServletRequest.getRequestURI(), IpAdrressUtil.getIpAdrress(httpServletRequest));
if (ipIsLock(IpAdrressUtil.getIpAdrress(httpServletRequest))){
log.info("ip访问被禁止={}",IpAdrressUtil.getIpAdrress(httpServletRequest));
ApiResult result = new ApiResult(ResultEnum.LOCK_IP);
returnJson(httpServletResponse, JSON.toJSONString(result));
return false;
}
if(!addRequestTime(IpAdrressUtil.getIpAdrress(httpServletRequest),httpServletRequest.getRequestURI())){
ApiResult result = new ApiResult(ResultEnum.LOCK_IP);
returnJson(httpServletResponse, JSON.toJSONString(result));
return false;
}
return true;
}

@Override
public void postHandle(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o, ModelAndView modelAndView) throws Exception {

}

@Override
public void afterCompletion(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o, Exception e) throws Exception {

}

/**
* @Description: 判断ip是否被禁用
* @author: shuyu.wang
* @date: 2019-10-12 13:08
* @param ip
* @return java.lang.Boolean
*/
private Boolean ipIsLock(String ip){
RedisUtil redisUtil=getRedisUtil();
if(redisUtil.hasKey(LOCK_IP_URL_KEY+ip)){
return true;
}
return false;
}
/**
* @Description: 记录请求次数
* @author: shuyu.wang
* @date: 2019-10-12 17:18
* @param ip
* @param uri
* @return java.lang.Boolean
*/
private Boolean addRequestTime(String ip,String uri){
String key=IP_URL_REQ_TIME+ip+uri;
RedisUtil redisUtil=getRedisUtil();
if (redisUtil.hasKey(key)){
long time=redisUtil.incr(key,(long)1);
if (time>=LIMIT_TIMES){
redisUtil.getLock(LOCK_IP_URL_KEY+ip,ip,IP_LOCK_TIME);
return false;
}
}else {
redisUtil.getLock(key,(long)1,1);
}
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();
}
}
}


}

代码中redis的使用的是分布式锁的形式,这样可以最大程度保证线程安全和功能的实现效果。代码中设置的是1S内同一个接口通过同一个ip访问5次,就将该ip禁用1个小时,根据自己项目需求可以自己适当修改,实现自己想要的功能;

redis分布式锁的关键代码:

/**
* @package: com.shuyu.blog.util
* @className: RedisUtil
* @description:
* @author: Shuyu.Wang
* @since: 0.1
**/
@Component
@Slf4j
public class RedisUtil {

private static final Long SUCCESS = 1L;

@Autowired
private RedisTemplate<String, Object> redisTemplate;
// =============================common============================



/**
* 获取锁
* @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<String> redisScript = new DefaultRedisScript<>(script, String.class);
Object result = redisTemplate.execute(redisScript, Collections.singletonList(lockKey), value, expireTime);
if (SUCCESS.equals(result)) {
return true;
}
} catch (Exception e) {
e.printStackTrace();
}
return false;
}

/**
* 释放锁
* @param lockKey
* @param value
* @return
*/
public boolean releaseLock(String lockKey, String value) {
String script = "if redis.call('get', KEYS[1]) == ARGV[1] then return redis.call('del', KEYS[1]) else return 0 end";
RedisScript<String> redisScript = new DefaultRedisScript<>(script, String.class);
Object result = redisTemplate.execute(redisScript, Collections.singletonList(lockKey), value);
if (SUCCESS.equals(result)) {
return true;
}
return false;
}

}

最后将上面自定义的拦截器通过registry.addInterceptor添加一下,就生效了;

@Configuration
@Slf4j
public class MyWebAppConfig extends WebMvcConfigurerAdapter {
@Bean
IpUrlLimitInterceptor getIpUrlLimitInterceptor(){
return new IpUrlLimitInterceptor();
}

@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(getIpUrlLimitInterceptor()).addPathPatterns("/**");
super.addInterceptors(registry);
}
}

自己可以写一个for循环来测试方面的功能,这里就不详细介绍了。

文章目录