携手创作,共同成长!这是我参与「掘金日新计划 · 8 月更文挑战」的第16天,点击查看活动详情
大家好! 我是慕歌,一只想教你学习 Spring Boot的野生coder! 欢迎来到慕歌的 Sping boot系列教程,希望通过这个教程带大家搭建基础的 Spring Boot项目,该教程所有知识点均来源于本人的真实开发!
前言
在上一节的学习中,慕歌带大家学习在Spring boot中如何获取请求的IP 以及IP地址。通过在线IP地址库和本地IP 地址库的方式,得到请求IP的地址,大家可根据自己的需求选择IP解析方式。如果为了更好的准确性可采用线上库,如果想要更快的解析到结果可采取ip2region,它是一款及其轻量快速的本地 IP库,支持多种语言开发。
获取到IP地址后,我们还可以考虑对每个用户的请求进行一定的限制,避免一些用户的恶意请求,对我们的服务器造成大量负担。那么通过IP进行拦截,就是一种简单有效的安全防护方式,当用户在一定时间内发起大量的请求的时候,就将该用户IP进行一段时间的封禁。
引入:
这里的过期策略,可以采用redis 的定时器功能,将IP 作为key 设置一个封禁时间。
<!-- redis 缓存操作 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
<!--连接池依赖-->
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-pool2</artifactId>
</dependency>
这里需要进行redis 连接的配置,以及redis 连接池的配置。
spring:
# redis 配置
redis:
# 地址
host: 39.99.202.124
# 端口
port: 6377
# 密码
password: zwd01221
# 超时
timeout: 20000
# 线程池
lettuce:
pool:
max-active: 50
min-idle: 3000
max-idle: 20
max-wait: -1
# 数据片
database: 4
开发:
如果想要进行IP的拦截处理,需要使用到上一章的拦截器。在请求进入的时候,就对IP进行处理,进行高效的拦截,不会对服务造成负担。我们在拦截器的前置请求中进行处理,这样前置处理未通过将不会对整个服务造成影响,只需要少量的处理,就完成请求防护。拦截的具体逻辑代码,如下:
/**
* 对ip 进行限制,防止IP大量请求
*/
@Slf4j
@Configuration
public class IpUrlLimitInterceptor implements HandlerInterceptor{
@Autowired
private RedisUtils redisUtils;
//锁ip
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=100;
//限制时间 单位:秒
private static final int IP_LOCK_TIME=300;
@Override
public boolean preHandle(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o) {
//请求端口 , ip
// log.info("request请求地址url={},ip={}", httpServletRequest.getRequestURI(), IPUtil.getIpAddr(httpServletRequest));
//IP是否被封锁
if (ipIsLock(IPUtil.getIpAddr(httpServletRequest))){
log.info("ip访问被禁止={}",IPUtil.getIpAddr(httpServletRequest));
//返回参数
returnJson(httpServletResponse, JSON.toJSONString(new ResultVo(ExceptionCode.IP_REPEAT_SUBMIT)));
return false;
}
//请求此时是否过多
if(!addRequestTime(IPUtil.getIpAddr(httpServletRequest),httpServletRequest.getRequestURI())){
returnJson(httpServletResponse, JSON.toJSONString(new ResultVo(ExceptionCode.IP_REPEAT_SUBMIT)));
return false;
}
return true;
}
@Override
public void postHandle(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o, ModelAndView modelAndView) {
}
@Override
public void afterCompletion(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o, Exception e) {
}
}
处理:
此处是对IP请求进行限制的逻辑,获取当前请求IP 已经发送请求的请求数,判断IP是否满足封禁条件,满足条件则进行封禁,并直接中断请求。具体逻辑处理代码,如下:
/**
* @Description: 判断ip是否被禁用
* @param ip
*/
private Boolean ipIsLock(String ip){
if(redisUtils.hasKey(LOCK_IP_URL_KEY+ip)){
return true;
}
return false;
}
/**
* @Description: 记录请求次数
* @param ip
* @param uri
*/
private Boolean addRequestTime(String ip,String uri){
//ip + url 组合
String key ="ip:" + IP_URL_REQ_TIME+ip+uri;
//查询
if (redisUtils.hasKey(key)){
//尝试将次属+1
long time = redisUtils.incr(key, 1);
//超次数
if (time >= LIMIT_TIMES){
redisUtils.set("ip:"+LOCK_IP_URL_KEY+ip,ip,IP_LOCK_TIME);
return false;
}
}else {
redisUtils.set(key,(long)1,1);
}
return true;
}
//通过json 回传数据
private void returnJson(HttpServletResponse response, String json) {
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("响应错误 ---> {}", e.getMessage(), e);
} finally {
if (writer != null) {
writer.close();
}
}
}
对IP 请求进行一定的处理后,我们就可以实现对不良请求的拦截。
结语
这一章的分享到这里就结束了,下一节中还将带来版本器Git,Gitee的分享!
如果您觉得本文不错,欢迎点赞支持,您的关注是我坚持的动力!