Java案例怎么实现白名单校验?

wen java案例 76

本文目录导读:

Java案例怎么实现白名单校验?

  1. 基础实现方案
  2. 企业级实现方案
  3. 性能优化方案
  4. application.yml配置示例
  5. 最佳实践建议

实现Java白名单校验通常有几种常见方案,我为你整理了一个完整的案例,包含从简单到企业级的实现方式。

基础实现方案

使用Set集合(最简单)

import java.util.HashSet;
import java.util.Set;
public class SimpleWhitelistValidator {
    private Set<String> whitelist;
    public SimpleWhitelistValidator() {
        // 初始化白名单
        whitelist = new HashSet<>();
        whitelist.add("192.168.1.1");
        whitelist.add("192.168.1.100");
        whitelist.add("10.0.0.1");
    }
    public boolean isValid(String ip) {
        return whitelist.contains(ip);
    }
    // 使用示例
    public static void main(String[] args) {
        SimpleWhitelistValidator validator = new SimpleWhitelistValidator();
        String testIp = "192.168.1.1";
        if (validator.isValid(testIp)) {
            System.out.println(testIp + " 在白名单中,允许访问");
        } else {
            System.out.println(testIp + " 不在白名单中,拒绝访问");
        }
    }
}

支持通配符匹配

import java.util.ArrayList;
import java.util.List;
public class PatternWhitelistValidator {
    private List<String> whitelistPatterns;
    public PatternWhitelistValidator() {
        whitelistPatterns = new ArrayList<>();
        // 支持通配符 * 和 ?
        whitelistPatterns.add("192.168.1.*");     // 匹配所有192.168.1.x
        whitelistPatterns.add("10.0.?.1");        // 匹配10.0.x.1
        whitelistPatterns.add("172.16.0.1-100");  // 匹配172.16.0.1到172.16.0.100
    }
    public boolean isValid(String ip) {
        for (String pattern : whitelistPatterns) {
            if (matchPattern(ip, pattern)) {
                return true;
            }
        }
        return false;
    }
    private boolean matchPattern(String ip, String pattern) {
        // 处理范围匹配
        if (pattern.contains("-")) {
            return matchRange(ip, pattern);
        }
        // 处理通配符匹配
        String regex = pattern
            .replace(".", "\\.")
            .replace("*", "[0-9]{1,3}")
            .replace("?", "[0-9]");
        return ip.matches(regex);
    }
    private boolean matchRange(String ip, String pattern) {
        String[] parts = pattern.split("-");
        if (parts.length != 2) return false;
        String baseIp = parts[0].substring(0, parts[0].lastIndexOf(".") + 1);
        int start = Integer.parseInt(parts[0].substring(parts[0].lastIndexOf(".") + 1));
        int end = Integer.parseInt(parts[1]);
        String ipBase = ip.substring(0, ip.lastIndexOf(".") + 1);
        if (!ipBase.equals(baseIp)) return false;
        int ipLast = Integer.parseInt(ip.substring(ip.lastIndexOf(".") + 1));
        return ipLast >= start && ipLast <= end;
    }
}

企业级实现方案

使用注解+AOP实现

// 1. 自定义注解
import java.lang.annotation.*;
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface WhitelistCheck {
    String[] value() default {};
    String param() default "ip";
}
// 2. 白名单配置类
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;
@Component
@ConfigurationProperties(prefix = "whitelist")
public class WhitelistConfig {
    private List<String> ips = new ArrayList<>();
    private List<String> patterns = new ArrayList<>();
    // getters and setters
    public List<String> getIps() { return ips; }
    public void setIps(List<String> ips) { this.ips = ips; }
    public List<String> getPatterns() { return patterns; }
    public void setPatterns(List<String> patterns) { this.patterns = patterns; }
}
// 3. AOP切面实现
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
import javax.servlet.http.HttpServletRequest;
import java.lang.reflect.Method;
@Aspect
@Component
public class WhitelistAspect {
    @Autowired
    private WhitelistConfig whitelistConfig;
    @Around("@annotation(whitelistCheck)")
    public Object checkWhitelist(ProceedingJoinPoint joinPoint, WhitelistCheck whitelistCheck) throws Throwable {
        HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder
            .getRequestAttributes()).getRequest();
        String clientIp = getClientIp(request);
        // 获取方法参数中的IP(如果存在)
        String paramIp = getParamIp(joinPoint, whitelistCheck.param());
        String ipToCheck = paramIp != null ? paramIp : clientIp;
        // 检查白名单
        if (!isWhitelisted(ipToCheck, whitelistCheck.value())) {
            throw new SecurityException("IP " + ipToCheck + " 不在白名单中");
        }
        return joinPoint.proceed();
    }
    private boolean isWhitelisted(String ip, String[] annotationWhitelist) {
        // 先检查注解中的白名单
        if (annotationWhitelist.length > 0) {
            for (String allowed : annotationWhitelist) {
                if (allowed.equals(ip)) return true;
            }
        }
        // 再检查配置文件中的白名单
        if (whitelistConfig.getIps().contains(ip)) return true;
        // 检查模式匹配
        for (String pattern : whitelistConfig.getPatterns()) {
            if (ip.matches(patternToRegex(pattern))) return true;
        }
        return false;
    }
    private String getClientIp(HttpServletRequest request) {
        String ip = request.getHeader("X-Forwarded-For");
        if (ip == null || ip.isEmpty() || "unknown".equalsIgnoreCase(ip)) {
            ip = request.getHeader("Proxy-Client-IP");
        }
        if (ip == null || ip.isEmpty() || "unknown".equalsIgnoreCase(ip)) {
            ip = request.getHeader("WL-Proxy-Client-IP");
        }
        if (ip == null || ip.isEmpty() || "unknown".equalsIgnoreCase(ip)) {
            ip = request.getRemoteAddr();
        }
        return ip;
    }
    private String getParamIp(ProceedingJoinPoint joinPoint, String paramName) {
        Object[] args = joinPoint.getArgs();
        MethodSignature signature = (MethodSignature) joinPoint.getSignature();
        Method method = signature.getMethod();
        String[] paramNames = signature.getParameterNames();
        for (int i = 0; i < paramNames.length; i++) {
            if (paramNames[i].equals(paramName)) {
                return (String) args[i];
            }
        }
        return null;
    }
    private String patternToRegex(String pattern) {
        return "^" + pattern
            .replace(".", "\\.")
            .replace("*", "[0-9]{1,3}")
            .replace("?", "[0-9]") + "$";
    }
}
// 4. 使用示例
@RestController
public class ApiController {
    @GetMapping("/api/sensitive")
    @WhitelistCheck(value = {"192.168.1.1"}, param = "ip")
    public String sensitiveApi(@RequestParam(required = false) String ip) {
        return "敏感数据";
    }
    @GetMapping("/api/admin")
    @WhitelistCheck
    public String adminApi() {
        return "管理接口";
    }
}

基于数据库的动态白名单

import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.Query;
import javax.persistence.*;
// 1. 实体类
@Entity
@Table(name = "whitelist")
public class WhitelistEntity {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;
    @Column(nullable = false, unique = true)
    private String ip;
    @Column(name = "ip_type")
    private String type; // SINGLE, RANGE, PATTERN
    @Column(name = "ip_range_start")
    private String rangeStart;
    @Column(name = "ip_range_end")
    private String rangeEnd;
    private String description;
    @Column(name = "is_active")
    private Boolean active = true;
    // getters and setters...
}
// 2. 仓库接口
public interface WhitelistRepository extends JpaRepository<WhitelistEntity, Long> {
    @Query("SELECT w FROM WhitelistEntity w WHERE w.active = true AND w.ip = ?1")
    List<WhitelistEntity> findByIp(String ip);
    @Query("SELECT w FROM WhitelistEntity w WHERE w.active = true AND w.type = ?1")
    List<WhitelistEntity> findByType(String type);
}
// 3. 缓存的服务类
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.stereotype.Service;
@Service
public class DynamicWhitelistService {
    @Autowired
    private WhitelistRepository repository;
    @Cacheable(value = "whitelist", key = "#ip")
    public boolean checkIp(String ip) {
        // 1. 精确匹配
        List<WhitelistEntity> exactMatches = repository.findByIp(ip);
        if (!exactMatches.isEmpty()) return true;
        // 2. 范围匹配
        List<WhitelistEntity> rangeRules = repository.findByType("RANGE");
        for (WhitelistEntity rule : rangeRules) {
            if (isInRange(ip, rule.getRangeStart(), rule.getRangeEnd())) {
                return true;
            }
        }
        // 3. 模式匹配
        List<WhitelistEntity> patternRules = repository.findByType("PATTERN");
        for (WhitelistEntity rule : patternRules) {
            if (ip.matches(patternToRegex(rule.getIp()))) {
                return true;
            }
        }
        return false;
    }
    private boolean isInRange(String ip, String start, String end) {
        long ipLong = ipToLong(ip);
        return ipLong >= ipToLong(start) && ipLong <= ipToLong(end);
    }
    private long ipToLong(String ipAddress) {
        String[] ipArray = ipAddress.split("\\.");
        long result = 0;
        for (int i = 0; i < ipArray.length; i++) {
            int power = 3 - i;
            result |= (Long.parseLong(ipArray[i]) << (power * 8));
        }
        return result;
    }
}

性能优化方案

使用布隆过滤器(适用于海量白名单)

import com.google.common.hash.BloomFilter;
import com.google.common.hash.Funnels;
import javax.annotation.PostConstruct;
import java.nio.charset.Charset;
public class BloomFilterWhitelist {
    private BloomFilter<String> bloomFilter;
    @PostConstruct
    public void init() {
        // 创建布隆过滤器,预计10000个元素,误报率0.01
        bloomFilter = BloomFilter.create(
            Funnels.stringFunnel(Charset.defaultCharset()),
            10000,
            0.01
        );
        // 加载白名单
        loadWhitelist();
    }
    private void loadWhitelist() {
        // 从配置文件或数据库加载白名单IP
        List<String> whitelistIps = getWhitelistIps();
        for (String ip : whitelistIps) {
            bloomFilter.put(ip);
        }
    }
    public boolean mightContain(String ip) {
        // 布隆过滤器可能误判,需要二次确认
        if (bloomFilter.mightContain(ip)) {
            return exactCheck(ip); // 二次精确检查
        }
        return false;
    }
    private boolean exactCheck(String ip) {
        // 在白名单中做精确查找
        return true; // 简化实现
    }
}

application.yml配置示例

whitelist:
  ips:
    - 192.168.1.1
    - 10.0.0.1
    - 172.16.0.1
  patterns:
    - 192.168.1.*
    - 10.0.*.1

最佳实践建议

  1. 缓存机制:使用Redis或本地缓存减少数据库查询
  2. 动态更新:支持运行时动态添加/移除白名单
  3. 日志记录:记录所有白名单校验的请求
  4. 性能监控:监控校验耗时,优化热点路径
  5. 降级策略:当白名单服务不可用时,有合理的降级方案

选择哪种方案取决于你的具体需求:

  • 小型应用:使用Set或List
  • 中型应用:使用注解+AOP
  • 大型应用:使用数据库+缓存+布隆过滤器

需要根据你的实际场景选择合适的实现方式。

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