Springboot拦截器+redis实现暴力请求拦截

在实际项目开发部署过程中,我们需要保证服务的安全性和可用性,当项目部署到服务器后,就要考虑服务被恶意请求和暴力攻击的情况。如何防止我们对外的接口被暴力攻击?下面的教程,通过Springboot提供的拦截器和Redis 针对IP在一定时间内访问的次数来将IP禁用,拒绝服务

1、新建RedisUtil

RedisUtil用于缓存数据

@Component
public class RedisUtil {
    @Resource
    private StringRedisTemplate stringRedisTemplate;

    /**
     * 取值
     * @param key
     * @return
     */
    public String get(final String key) {
        return stringRedisTemplate.opsForValue().get(key);
    }

    /**
     * 是否存在键
     * @param key
     * @return
     */
    public boolean hasKey(final String key){
        return Boolean.TRUE.equals(stringRedisTemplate.hasKey(key));
    }

    /**
     * 根据前缀正则获取所有键
     * @param prefix
     * @return
     */
    public Set<String> getKeysByPattern(final String prefix){
        Set<String> keys = this.getKeys(prefix.concat("*"));
        return keys;
    }

    private Set<String> getKeys(String pattern){
        Set<String> keys = stringRedisTemplate.execute((RedisCallback<Set<String>>) connection -> {
            Set<String> keysTmp = new HashSet<>();
            Cursor<byte[]> cursor = connection.scan(new ScanOptions.ScanOptionsBuilder().match(pattern).count(1000).build());
            while (cursor.hasNext()) {
                keysTmp.add(new String(cursor.next()));
            }
            return keysTmp;
        });
        return keys;
    }

    /**
     * 设值,时间单位为秒
     * @param key
     * @param value
     * @param timeout
     * @return
     */
    public boolean set(final String key, String value, long timeout, TimeUnit timeUnit) {
        boolean result = false;
        try {
            stringRedisTemplate.opsForValue().set(key, value, timeout, timeUnit);
            result = true;
        } catch (Exception e) {
            e.printStackTrace();
        }
        return result;
    }

    /**
     * 删除一个键值
     * @param key
     * @return
     */
    public boolean delete(final String key) {
        boolean result = false;
        try {
            stringRedisTemplate.delete(key);
            result = true;
        } catch (Exception e) {
            e.printStackTrace();
        }
        return result;
    }

    /**
     * 批量删除键值
     * @param keys
     * @return
     */
    public boolean delete(Collection<String> keys) {
        boolean result = false;
        try {
            stringRedisTemplate.delete(keys);
            result = true;
        } catch (Exception e) {
            e.printStackTrace();
        }
        return result;
    }

    /**
     * 自增
     * @param key
     * @param value
     * @param timeout
     * @param timeUnit
     */
    public void increment(String key, String value, long timeout, TimeUnit timeUnit){
        if(hasKey(key)){
           stringRedisTemplate.opsForValue().increment(key, Long.parseLong(value));
        }else {
            set(key, value, timeout, timeUnit);
        }
    }

    /**
     * 根据正则表达式删除所有键值
     * @param pattern
     * @return
     */
    public boolean deleteAllByPattern(final String pattern){
        Set<String> keysByPattern = getKeysByPattern(pattern);
        return delete(keysByPattern);
    }
}
2、新建IPUtil

IPUtil用于获取客户端请求的IP

@Component
public class IPUtil {

    public String getIpAddr(HttpServletRequest request) {
        String ipAddress;
        try {
            ipAddress = request.getHeader("x-forwarded-for");
            if (ipAddress == null || ipAddress.length() == 0
                    || "unknown".equalsIgnoreCase(ipAddress)) {
                ipAddress = request.getHeader("Proxy-Client-IP");
            }
            if (ipAddress == null || ipAddress.length() == 0
                    || "unknown".equalsIgnoreCase(ipAddress)) {
                ipAddress = request.getHeader("WL-Proxy-Client-IP");
            }
            if (ipAddress == null || ipAddress.length() == 0
                    || "unknown".equalsIgnoreCase(ipAddress)) {
                ipAddress = request.getRemoteAddr();
                if (ipAddress.equals("127.0.0.1")) {
                    // 根据网卡取本机配置的IP
                    InetAddress inet = null;
                    try {
                        inet = InetAddress.getLocalHost();
                    } catch (UnknownHostException e) {
                        e.printStackTrace();
                    }
                    ipAddress = inet.getHostAddress();
                }
            }
            // 对于通过多个代理的情况,第一个IP为客户端真实IP,多个IP按照','分割
            if (ipAddress != null && ipAddress.length() > 15) { // "***.***.***.***".length()
                // = 15
                if (ipAddress.indexOf(",") > 0) {
                    ipAddress = ipAddress.substring(0, ipAddress.indexOf(","));
                }
            }
        } catch (Exception e) {
            ipAddress = "";
        }

        return ipAddress;
    }
}
3、定义拦截器

拦截器代码如下,该拦截器将记录一天内的单个IP的请求数量,当请求数量达到1000时,不再处理请求,直接响应报错

@Component
public class ViolentRequestInterceptor implements HandlerInterceptor {
    //配置文件配置最大请求数量
    //@Value("${violentRequest.maxCount}")
    //private int maxCount;

    private final int maxCount = 1000;

    @Autowired
    private RedisUtil redisUtil;
    @Autowired
    private IPUtil ipUtil;

    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        String ipAddr = ipUtil.getIpAddr(request);
        String key = ConstUtil.SYS_PREVENT_VIOLENT_REQUESTS + ipAddr;
        if(!redisUtil.hasKey(key)){
            redisUtil.increment(key, String.valueOf(1), 1, TimeUnit.DAYS);
        }else {
            long count = Long.parseLong(redisUtil.get(key));
            if(count>maxCount){
                JSONObject json = (JSONObject) JSONObject.toJSON(Result.failure(HAVIOR_INVOKE_ERROR));
                response.setContentType("application/json;charset=UTF-8");
                response.getWriter().println(json);
                return false;
            }
            redisUtil.increment(key, String.valueOf(1), 1, TimeUnit.DAYS);
        }
        return true;
    }
}
4、配置拦截器

配置拦截器对所有请求生效并设置优先级为1

@Configuration
public class InterceptorConfig implements WebMvcConfigurer {
    @Autowired
    private ViolentRequestInterceptor violentRequestInterceptor;
    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        registry.addInterceptor(violentRequestInterceptor).addPathPatterns("/**").order(1);
    }

你可能感兴趣的:(Java,SpringBoot,spring,boot,redis,后端)