Java IP封禁案例如何实现

wen java案例 28

本文目录导读:

Java IP封禁案例如何实现

  1. 基于内存的简易IP封禁
  2. 基于Redis的分布式IP封禁
  3. 完整的Spring Boot应用示例
  4. 高级封禁策略
  5. 使用建议

我来详细讲解Java实现IP封禁的几种常见方案和案例。

基于内存的简易IP封禁

基础实现示例

import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.atomic.AtomicInteger;
public class SimpleIpBlocker {
    // IP访问次数记录
    private static ConcurrentHashMap<String, AtomicInteger> accessCount = new ConcurrentHashMap<>();
    // 封禁的IP集合
    private static ConcurrentHashMap<String, Long> blockedIps = new ConcurrentHashMap<>();
    // 配置参数
    private static final int MAX_ATTEMPTS = 10;  // 最大尝试次数
    private static final long BLOCK_DURATION = 60000;  // 封禁时长(毫秒)
    /**
     * 检查IP是否被封禁
     */
    public static boolean isBlocked(String ip) {
        // 检查是否在封禁列表中
        Long blockTime = blockedIps.get(ip);
        if (blockTime != null) {
            // 检查封禁是否过期
            if (System.currentTimeMillis() - blockTime > BLOCK_DURATION) {
                // 封禁过期,解除封禁
                blockedIps.remove(ip);
                accessCount.remove(ip);
                return false;
            }
            return true;
        }
        return false;
    }
    /**
     * 记录IP访问
     */
    public static void recordAccess(String ip) {
        if (isBlocked(ip)) {
            return;
        }
        // 原子递增访问计数
        AtomicInteger counter = accessCount.computeIfAbsent(ip, k -> new AtomicInteger(0));
        int count = counter.incrementAndGet();
        // 超过阈值则封禁
        if (count >= MAX_ATTEMPTS) {
            blockedIps.put(ip, System.currentTimeMillis());
            System.out.println("IP " + ip + " 已被封禁,访问次数: " + count);
        }
    }
    /**
     * 手动解封IP
     */
    public static void unblockIp(String ip) {
        blockedIps.remove(ip);
        accessCount.remove(ip);
        System.out.println("IP " + ip + " 已被解封");
    }
}

过滤器实现

import javax.servlet.*;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
public class IpBlockFilter implements Filter {
    @Override
    public void init(FilterConfig filterConfig) throws ServletException {
        // 初始化配置
    }
    @Override
    public void doFilter(ServletRequest request, ServletResponse response, 
                        FilterChain chain) throws IOException, ServletException {
        HttpServletRequest httpRequest = (HttpServletRequest) request;
        HttpServletResponse httpResponse = (HttpServletResponse) response;
        // 获取客户端IP
        String clientIp = getClientIp(httpRequest);
        // 检查IP是否被封禁
        if (SimpleIpBlocker.isBlocked(clientIp)) {
            httpResponse.setStatus(403);
            httpResponse.getWriter().write("您的IP已被封禁");
            return;
        }
        // 记录访问
        SimpleIpBlocker.recordAccess(clientIp);
        // 继续执行后续操作
        chain.doFilter(request, response);
    }
    /**
     * 获取真实客户端IP
     */
    private String getClientIp(HttpServletRequest request) {
        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();
        }
        return ip;
    }
    @Override
    public void destroy() {
        // 清理资源
    }
}

基于Redis的分布式IP封禁

Redis实现方案

import redis.clients.jedis.Jedis;
import redis.clients.jedis.JedisPool;
import redis.clients.jedis.Pipeline;
import redis.clients.jedis.Response;
public class RedisIpBlocker {
    private JedisPool jedisPool;
    // 缓存配置
    private static final String BLOCK_PREFIX = "ip:block:";
    private static final String COUNT_PREFIX = "ip:count:";
    private static final int MAX_ATTEMPTS = 10;
    private static final int BLOCK_DURATION = 600;  // 秒
    private static final int COUNT_WINDOW = 60;     // 统计窗口(秒)
    public RedisIpBlocker(JedisPool jedisPool) {
        this.jedisPool = jedisPool;
    }
    /**
     * 检查IP是否被封禁
     */
    public boolean isBlocked(String ip) {
        try (Jedis jedis = jedisPool.getResource()) {
            String blockKey = BLOCK_PREFIX + ip;
            return jedis.exists(blockKey);
        }
    }
    /**
     * 记录访问并检查是否需要封禁
     */
    public boolean checkAndRecord(String ip) {
        try (Jedis jedis = jedisPool.getResource()) {
            String countKey = COUNT_PREFIX + ip;
            // 使用Lua脚本保证原子性
            String luaScript = 
                "local count = redis.call('incr', KEYS[1])\n" +
                "redis.call('expire', KEYS[1], ARGV[1])\n" +
                "if count >= tonumber(ARGV[2]) then\n" +
                "    redis.call('setex', KEYS[2], ARGV[3], '1')\n" +
                "    return 1\n" +
                "end\n" +
                "return 0";
            Object result = jedis.eval(luaScript, 2, 
                countKey, 
                BLOCK_PREFIX + ip,
                String.valueOf(COUNT_WINDOW),
                String.valueOf(MAX_ATTEMPTS),
                String.valueOf(BLOCK_DURATION));
            return "1".equals(result.toString());
        }
    }
    /**
     * 解封IP
     */
    public void unblockIp(String ip) {
        try (Jedis jedis = jedisPool.getResource()) {
            Pipeline pipeline = jedis.pipelined();
            pipeline.del(BLOCK_PREFIX + ip);
            pipeline.del(COUNT_PREFIX + ip);
            pipeline.sync();
        }
    }
    /**
     * 获取IP访问次数
     */
    public long getAccessCount(String ip) {
        try (Jedis jedis = jedisPool.getResource()) {
            String count = jedis.get(COUNT_PREFIX + ip);
            return count != null ? Long.parseLong(count) : 0;
        }
    }
    /**
     * 获取被封禁的IP列表
     */
    public Set<String> getBlockedIps() {
        try (Jedis jedis = jedisPool.getResource()) {
            return jedis.keys(BLOCK_PREFIX + "*");
        }
    }
}

Spring Boot集成Redis配置

import org.springframework.beans.factory.annotation.Value;
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.RedisTemplate;
import org.springframework.data.redis.serializer.StringRedisSerializer;
import org.springframework.data.redis.core.script.DefaultRedisScript;
import org.springframework.data.redis.core.script.RedisScript;
@Configuration
public class RedisConfig {
    @Bean
    public RedisTemplate<String, String> redisTemplate(RedisConnectionFactory factory) {
        RedisTemplate<String, String> template = new RedisTemplate<>();
        template.setConnectionFactory(factory);
        template.setKeySerializer(new StringRedisSerializer());
        template.setValueSerializer(new StringRedisSerializer());
        return template;
    }
    @Bean
    public RedisScript<Long> ipBlockScript() {
        String script = 
            "local count = redis.call('incr', KEYS[1])\n" +
            "redis.call('expire', KEYS[1], ARGV[1])\n" +
            "if count >= tonumber(ARGV[2]) then\n" +
            "    redis.call('setex', KEYS[2], ARGV[3], '1')\n" +
            "    return 1\n" +
            "end\n" +
            "return 0";
        return new DefaultRedisScript<>(script, Long.class);
    }
}

完整的Spring Boot应用示例

配置类

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 IpBlockInterceptor ipBlockInterceptor;
    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        registry.addInterceptor(ipBlockInterceptor)
                .addPathPatterns("/api/**")
                .excludePathPatterns("/api/health", "/api/unblock");
    }
}

拦截器实现

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.web.servlet.HandlerInterceptor;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
@Component
public class IpBlockInterceptor implements HandlerInterceptor {
    @Autowired
    private RedisIpBlocker redisIpBlocker;
    @Override
    public boolean preHandle(HttpServletRequest request, 
                            HttpServletResponse response, 
                            Object handler) throws Exception {
        String clientIp = getClientIp(request);
        // 检查是否被封禁
        if (redisIpBlocker.isBlocked(clientIp)) {
            response.setStatus(403);
            response.setContentType("application/json;charset=UTF-8");
            response.getWriter().write("{\"code\":403,\"message\":\"IP已被封禁\"}");
            return false;
        }
        // 记录访问并检查
        if (redisIpBlocker.checkAndRecord(clientIp)) {
            response.setStatus(403);
            response.setContentType("application/json;charset=UTF-8");
            response.getWriter().write("{\"code\":403,\"message\":\"访问频繁,IP已被封禁\"}");
            return false;
        }
        return true;
    }
    private String getClientIp(HttpServletRequest request) {
        // 获取真实IP的代码同上
        // ...
    }
}

封禁管理接口

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
@RestController
@RequestMapping("/api/ip-block")
public class IpBlockController {
    @Autowired
    private RedisIpBlocker redisIpBlocker;
    /**
     * 手动封禁IP
     */
    @PostMapping("/block")
    public String blockIp(@RequestParam String ip,
                         @RequestParam(required = false, defaultValue = "3600") long duration) {
        // 实现手动封禁
        return "IP已封禁";
    }
    /**
     * 解封IP
     */
    @PostMapping("/unblock")
    public String unblockIp(@RequestParam String ip) {
        redisIpBlocker.unblockIp(ip);
        return "IP已解封";
    }
    /**
     * 查询IP状态
     */
    @GetMapping("/status")
    public Map<String, Object> getIpStatus(@RequestParam String ip) {
        Map<String, Object> result = new HashMap<>();
        result.put("ip", ip);
        result.put("blocked", redisIpBlocker.isBlocked(ip));
        result.put("accessCount", redisIpBlocker.getAccessCount(ip));
        return result;
    }
}

高级封禁策略

滑动窗口算法

public class SlidingWindowBlocker {
    private static final int WINDOW_SIZE = 60;  // 窗口大小(秒)
    private static final int MAX_REQUESTS = 100; // 最大请求数
    private final RedisTemplate<String, String> redisTemplate;
    public boolean checkSlidingWindow(String ip) {
        String key = "ip:sliding:" + ip;
        long now = System.currentTimeMillis() / 1000;
        DefaultRedisScript<Long> script = new DefaultRedisScript<>();
        script.setScriptText(
            "redis.call('zremrangebyscore', KEYS[1], 0, ARGV[1] - 3600)\n" +
            "local count = redis.call('zcard', KEYS[1])\n" +
            "if count < tonumber(ARGV[2]) then\n" +
            "    redis.call('zadd', KEYS[1], ARGV[1], ARGV[1])\n" +
            "    redis.call('expire', KEYS[1], ARGV[3])\n" +
            "    return 0\n" +
            "end\n" +
            "return 1"
        );
        script.setResultType(Long.class);
        List<String> keys = Arrays.asList(key);
        Long result = redisTemplate.execute(script, keys, 
            String.valueOf(now),
            String.valueOf(MAX_REQUESTS),
            String.valueOf(WINDOW_SIZE));
        return result == 1;
    }
}

动态阈值调整

public class DynamicThresholdBlocker {
    // 动态调整访问阈值
    public int getThreshold(String ip) {
        // 根据IP的访问历史动态调整
        String historyKey = "ip:history:" + ip;
        // 获取过去24小时的访问次数
        String count = redisTemplate.opsForValue().get(historyKey);
        long totalCount = count != null ? Long.parseLong(count) : 0;
        // 根据访问频率动态调整阈值
        if (totalCount > 10000) {
            return 5;  // 高频IP,降低阈值
        } else if (totalCount > 1000) {
            return 10;
        } else {
            return 20;  // 正常IP,使用默认阈值
        }
    }
}

使用建议

部署配置

# application.yml
ip-block:
  enabled: true
  max-attempts: 10
  block-duration: 600
  count-window: 60
  whitelist:
    - 127.0.0.1
    - 192.168.1.0/24

最佳实践

  1. 多层防护:结合Nginx、防火墙等多层防护
  2. 分段处理:不同接口使用不同的封禁策略
  3. 日志记录:记录所有封禁事件以便审计
  4. 白名单机制:对内网IP、健康检查等放行
  5. 自动解封:设置合理的自动解封时间

这个完整的Java IP封禁方案可以根据实际需求选择合适的实现方式。

抱歉,评论功能暂时关闭!