栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 后端开发 > Java

SpringBoot中使用注解方式拦截恶意访问的IP

Java 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

SpringBoot中使用注解方式拦截恶意访问的IP

前言

针对有人频繁重复调用接口,进行恶意访问,例如使用爬虫一类,对服务造成巨大负担,所以需要在接口上进行拦截。

这里使用自定义注解的方式进行拦截,方便重复调用使用。

我采用的是使用单机SpringBoot + Redis的方式进行处理,没有用分布式锁

Redis的配置需要自行配置

一、后端代码

1. 依赖引入 pom.xml和 yml配置文件中配置Redis


    org.springframework.boot
    spring-boot-starter-data-redis



    org.apache.commons
    commons-pool2
spring:
  # redis 配置
  redis:
    # 地址
    host: 127.0.0.1
    # 端口,默认为6379
    port: 6379
    # 数据库索引
    database: 0
    # 密码
    password: 123456
    # 连接超时时间
    timeout: 10s
    lettuce:
      pool:
        # 连接池中的最小空闲连接
        min-idle: 0
        # 连接池中的最大空闲连接
        max-idle: 8
        # 连接池的最大数据库连接数
        max-active: 8
        # #连接池最大阻塞等待时间(使用负值表示没有限制)
        max-wait: -1ms

 

2. redis部分

Redis工具类

Redis工具类,方便Redis的基础操作

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.BoundSetOperations;
import org.springframework.data.redis.core.HashOperations;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.ValueOperations;
import org.springframework.stereotype.Component;
import java.util.*;
import java.util.concurrent.TimeUnit;


@SuppressWarnings(value = {"unchecked", "rawtypes"})
@Component
public class Redisutil {

    @Autowired
    public RedisTemplate redisTemplate;

    
    public  void setCacheObject(final String key, final T value) {
        redisTemplate.opsForValue().set(key, value);
    }

    
    public  void setCacheObject(final String key, final T value, final Integer timeout, final TimeUnit timeUnit) {
        redisTemplate.opsForValue().set(key, value, timeout, timeUnit);
    }

    
    public boolean expire(final String key, final long timeout) {
        return expire(key, timeout, TimeUnit.SECONDS);
    }

    
    public boolean expire(final String key, final long timeout, final TimeUnit unit) {
        return redisTemplate.expire(key, timeout, unit);
    }

    
    public  T getCacheObject(final String key) {
        ValueOperations operation = redisTemplate.opsForValue();
        return operation.get(key);
    }

    
    public boolean deleteObject(final String key) {
        return redisTemplate.delete(key);
    }

    
    public long deleteObject(final Collection collection) {
        return redisTemplate.delete(collection);
    }

    
    public  long setCacheList(final String key, final List dataList) {
        Long count = redisTemplate.opsForList().rightPushAll(key, dataList);
        return count == null ? 0 : count;
    }

    
    public  List getCacheList(final String key) {
        return redisTemplate.opsForList().range(key, 0, -1);
    }

    
    public  BoundSetOperations setCacheSet(final String key, final Set dataSet) {
        BoundSetOperations setOperation = redisTemplate.boundSetOps(key);
        Iterator it = dataSet.iterator();
        while (it.hasNext()) {
            setOperation.add(it.next());
        }
        return setOperation;
    }

    
    public  Set getCacheSet(final String key) {
        return redisTemplate.opsForSet().members(key);
    }

    
    public  void setCacheMap(final String key, final Map dataMap) {
        if (dataMap != null) {
            redisTemplate.opsForHash().putAll(key, dataMap);
        }
    }

    
    public  Map getCacheMap(final String key) {
        return redisTemplate.opsForHash().entries(key);
    }

    
    public  void setCacheMapValue(final String key, final String hKey, final T value) {
        redisTemplate.opsForHash().put(key, hKey, value);
    }

    
    public  T getCacheMapValue(final String key, final String hKey) {
        HashOperations opsForHash = redisTemplate.opsForHash();
        return opsForHash.get(key, hKey);
    }

    
    public  List getMultiCacheMapValue(final String key, final Collection hKeys) {
        return redisTemplate.opsForHash().multiGet(key, hKeys);
    }

    
    public Collection keys(final String pattern) {
        return redisTemplate.keys(pattern);
    }

    
    public Long incr(String key, long delta) {
        return redisTemplate.opsForValue().increment(key, delta);
    }
}
 

RedisTemplateConfig配置类

添加这个配置类,防止存Redis的时候key或value乱码

import com.fasterxml.jackson.annotation.JsonAutoDetect;
import com.fasterxml.jackson.annotation.PropertyAccessor;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.core.*;
import org.springframework.data.redis.serializer.Jackson2JsonRedisSerializer;
import org.springframework.data.redis.serializer.StringRedisSerializer;


@Configuration
public class RedisTemplateConfig{
    
    @Bean
    public RedisTemplate redisTemplate(RedisConnectionFactory factory) {

        RedisTemplate template = new RedisTemplate<>();
        // 配置连接工厂
        template.setConnectionFactory(factory);

        //使用Jackson2JsonRedisSerializer来序列化和反序列化redis的value值(默认使用JDK的序列化方式)
        Jackson2JsonRedisSerializer jacksonSeial = new Jackson2JsonRedisSerializer(Object.class);

        ObjectMapper om = new ObjectMapper();
        // 指定要序列化的域,field,get和set,以及修饰符范围,ANY是都有包括private和public
        om.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
        // 指定序列化输入的类型,类必须是非final修饰的,final修饰的类,比如String,Integer等会跑出异常
        om.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);
        jacksonSeial.setObjectMapper(om);

        // 值采用json序列化
        template.setValueSerializer(jacksonSeial);
        //使用StringRedisSerializer来序列化和反序列化redis的key值
        template.setKeySerializer(new StringRedisSerializer());

        // 设置hash key 和value序列化模式
        template.setHashKeySerializer(new StringRedisSerializer());
        template.setHashValueSerializer(jacksonSeial);
        template.afterPropertiesSet();

        return template;
    }

    
    @Bean
    public HashOperations hashOperations(RedisTemplate redisTemplate) {
        return redisTemplate.opsForHash();
    }

    
    @Bean
    public ValueOperations valueOperations(RedisTemplate redisTemplate) {
        return redisTemplate.opsForValue();
    }

    
    @Bean
    public ListOperations listOperations(RedisTemplate redisTemplate) {
        return redisTemplate.opsForList();
    }

    
    @Bean
    public SetOperations setOperations(RedisTemplate redisTemplate) {
        return redisTemplate.opsForSet();
    }

    
    @Bean
    public ZSetOperations zSetOperations(RedisTemplate redisTemplate) {
        return redisTemplate.opsForZSet();
    }
}
3. 接口返回结果封装对象
import java.util.HashMap;

public class Result extends HashMap {
    private static final long serialVersionUID = 1L;

    
    public static final String CODE_TAG = "code";

    
    public static final String MSG_TAG = "msg";

    
    public static final String DATA_TAG = "data";

    
    public Result() {
    }

    
    public Result(int code, String msg) {
        super.put(CODE_TAG, code);
        super.put(MSG_TAG, msg);
    }

    
    public Result(int code, String msg, Object data) {
        super.put(CODE_TAG, code);
        super.put(MSG_TAG, msg);
        if (data != null) {
            super.put(DATA_TAG, data);
        }
    }

    
    public static Result success() {
        return Result.success("操作成功");
    }

    
    public static Result success(Object data) {
        return Result.success("操作成功", data);
    }

    
    public static Result success(String msg) {
        return Result.success(msg, null);
    }

    
    public static Result success(String msg, Object data) {
        return new Result(200, msg, data);
    }

    
    public static Result error() {
        return Result.error("操作失败");
    }

    
    public static Result error(String msg) {
        return Result.error(msg, null);
    }

    
    public static Result error(String msg, Object data) {
        return new Result(500, msg, data);
    }

    
    public static Result error(int code, String msg) {
        return new Result(code, msg, null);
    }

    
    @Override
    public Result put(String key, Object value) {
        super.put(key, value);
        return this;
    }
}

4. 拦截部分

自定义注解

import java.lang.annotation.*;
import java.util.concurrent.TimeUnit;


@Target({ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface AccessLimit {
    
    long seconds() default 60;
    
    long maxCount() default 100;
    
    TimeUnit timeUnit() default TimeUnit.SECONDS;
    
    String apiName() default "";
}

拦截恶意访问接口 - 逻辑处理

import com.alibaba.fastjson.JSONObject;
import com.smart.soulsoup.utils.commons.Result;
import com.smart.soulsoup.utils.redis.RedisCache;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.servlet.HandlerInterceptor;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.concurrent.TimeUnit;


@Component
public class BrushProofHandler implements HandlerInterceptor {
    @Autowired
    RedisCache redisCache;
    private static final String REDIS_KEY_PREFIX= "api::interface";

    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        // 判断是不是方法
        if (handler instanceof HandlerMethod) {
            HandlerMethod method = (HandlerMethod) handler;
            // 判断方法中是否有此注解
            AccessLimit accessLimit = method.getMethodAnnotation(AccessLimit.class);
            if (accessLimit != null) {
                // 时间限制大小
                long seconds = accessLimit.seconds();
                // 最大次数
                long maxCount = accessLimit.maxCount();
                // 时间单位
                TimeUnit timeUnit = accessLimit.timeUnit();
                // 接口名称
                String apiName = accessLimit.apiName();
                // 获取调用者ip
                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();
                }
				// redis的完整key
                String redisKey = REDIS_KEY_PREFIX + "::" + apiName + ":: " + ip;
                Integer count = (Integer) redisCache.getCacheObject(redisKey);
                if (count == null) {
                    redisCache.setCacheObject(redisKey, 1, Integer.valueOf(String.valueOf(seconds)), timeUnit);
                } else if (count < maxCount) {
                    // 在时间允许范围内
                    redisCache.incr(redisKey, 1);
                } else {
                    response.setCharacterEncoding("UTF-8");
                    response.setContentType("application/json; charset=utf-8");
                    Result result = Result.error("请你注意你的行为!不要频繁访问!");
                    response.getWriter().write(JSONObject.toJSONString(result));
                    return false;
                }
            }
        }
        return true;
    }
}

拦截恶意访问接口配置 

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;


@Configuration
public class WebConfig implements WebMvcConfigurer {
    @Autowired
    private BrushProofHandler brushProofHandler;
    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        registry.addInterceptor(brushProofHandler);
    }
}

5. Controller接口
import com.smart.demolsoup.service.ISoulService;
import com.smart.demo.utils.api.AccessLimit;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.*;
import java.util.concurrent.TimeUnit;

@Controller
public class TestController {
    
    @AccessLimit(seconds = 30, maxCount = 150, apiName = "getStr", timeUnit = TimeUnit.MINUTES)
    @ResponseBody
    @GetMapping("/getStr”)
    public String getStr() {
        return “SUCCESS;
    }
}

二、测试

转载请注明:文章转载自 www.mshxw.com
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号