Spring Boot(八) 快速实现恶意请求拦截

447 阅读3分钟

携手创作,共同成长!这是我参与「掘金日新计划 · 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的分享!
如果您觉得本文不错,欢迎点赞支持,您的关注是我坚持的动力!