java-接口级IP白名单配置指南:精准控制接口访问权限

117 阅读4分钟

🛡️ 接口级IP白名单配置指南:精准控制接口访问权限

本文介绍了一种基于Spring AOP的接口级IP白名单解决方案,帮助开发者快速实现精细化的接口访问控制。

✨ 核心价值

痛点场景

  • 某些敏感页面需要限制特定IP访问
  • 其他普通页面仍需保持正常开放
  • 需要动态配置,避免每次修改都重新部署

我们的解决方案: 通过注解+配置的方式,实现接口级别的IP白名单控制,配置热生效,无需重启服务。

🏗️ 架构原理

核心组件

组件职责说明
@RestrictToInternalIp标记需要IP控制的接口自定义注解
IpAccessAspectIP校验切面AOP实现,核心逻辑
ISysConfigService配置读取服务动态获取IP白名单
sys_config存储IP白名单配置数据库配置中心

工作流程

ip白名单流程图.png

⚙️ 快速配置

数据库配置

sys_config 表中配置IP白名单:

-- 查看当前配置
SELECT * FROM sys_config WHERE config_key = 'sys.access.internalIpList';

-- 更新配置(示例)
UPDATE sys_config 
SET config_value = '192.168.,10.,127.0.0.1'
WHERE config_key = 'sys.access.internalIpList';

配置格式说明

  • 多个IP段用英文逗号分隔
  • 支持CIDR格式或通配符格式
  • 示例:192.168.1.,10.0.0.0/8,172.16.0.0/12

默认安全配置

系统预置了常见内网IP段,开箱即用:

192.168.,10.,172.16.,172.17.,172.18.,172.19.,172.20.,172.21.,172.22.,172.23.,172.24.,172.25.,172.26.,172.27.,172.28.,172.29.,172.30.,172.31.,127.0.0.1

🚀 如何使用

1. 添加注解保护接口

@RestController
@RequestMapping("/api/sensitive")
public class SensitiveDataController {
    
    /**
     * 此接口仅限内网IP访问
     */
    @GetMapping("/admin-data")
    @RestrictToInternalIp  // 👈 关键注解
    public ResponseEntity<AdminData> getAdminData() {
        // 只有白名单IP能访问到这里
        return ResponseEntity.ok(adminService.getSensitiveData());
    }
    
    /**
     * 此接口对外开放
     */
    @GetMapping("/public-data")
    public ResponseEntity<PublicData> getPublicData() {
        // 所有IP均可访问
        return ResponseEntity.ok(publicService.getData());
    }
}

2. 核心实现代码

定义注解

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface RestrictToInternalIp {
    // 可扩展:添加错误信息、日志级别等参数
}

AOP切面逻辑

@Aspect
@Component
@Slf4j
public class IpAccessAspect {

    @Autowired
    private ISysConfigService configService;
    
    private static final String INTERNAL_IP_CONFIG_KEY = "sys.access.internalIpList";

    @Before("@annotation(restrictToInternalIp)")
    public void checkIpAccess(RestrictToInternalIp restrictToInternalIp) {
        HttpServletRequest request = ((ServletRequestAttributes) 
            RequestContextHolder.getRequestAttributes()).getRequest();
            
        String clientIp = IpUtils.getIpAddr(request);
        String internalIpList = configService.selectConfigByKey(INTERNAL_IP_CONFIG_KEY);
        
        log.debug("IP白名单校验: clientIp={}, whiteList={}", clientIp, internalIpList);
        
        if (!IpUtils.isMatchedIp(internalIpList, clientIp)) {
            log.warn("IP访问被拒绝: {}", clientIp);
            throw new RuntimeException("访问被拒绝:您的IP不在白名单内,请联系管理员");
        }
        
        log.debug("IP白名单校验通过: {}", clientIp);
    }
}

🐳 容器化部署注意事项

在Docker/K8s环境中,确保正确获取真实客户端IP:

Spring Boot配置

server:
  forward-headers-strategy: framework
  tomcat:
    remote-ip-header: x-forwarded-for
    protocol-header: x-forwarded-proto

Nginx配置示例

location / {
    proxy_set_header X-Real-IP $remote_addr;
    proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
    proxy_set_header X-Forwarded-Proto $scheme;
}

🔧 故障排查指南

常见问题及解决方案

问题现象可能原因解决方案
❌ 所有请求IP相同反向代理未传真实IP检查Nginx配置,确保传递X-Forwarded-For
❌ 白名单不生效注解未正确添加检查Controller方法是否有@RestrictToInternalIp
❌ 配置读取失败数据库连接问题检查配置表数据是否存在
❌ 本地访问被拒127.0.0.1不在白名单确认白名单包含127.0.0.1

调试技巧

  1. 开启DEBUG日志查看IP校验过程
  2. 手动验证IP匹配
    String testIp = "192.168.1.100";
    String whiteList = "192.168.,10.";
    boolean matched = IpUtils.isMatchedIp(whiteList, testIp); // 应该返回true
    

💡 进阶扩展建议

这个基础框架可以轻松扩展:

1. 黑白名单组合

// 扩展注解
public @interface IpAccessControl {
    boolean isWhiteList() default true;
    String configKey() default "sys.access.internalIpList";
}

2. 按角色差异化控制

@IpAccessControl(role = "ADMIN", ipList = "admin.ip.list")
@IpAccessControl(role = "USER", ipList = "user.ip.list")

3. 时间段控制

@IpAccessControl(timeRange = "09:00-18:00") // 仅工作时间可访问

🛡️ 安全最佳实践

  1. 定期审计:每月审查一次IP白名单
  2. 最小权限:只为真正敏感的接口添加保护
  3. 日志完备:记录所有访问拒绝事件
  4. 配置备份:备份重要的IP白名单配置
  5. 监控告警:对异常访问模式设置告警

💬 交流时间

🤔 大家觉得这种方案怎么样?

  • 你们项目中是如何处理IP访问控制的?
  • 有没有更好的实现思路或踩坑经验?
  • 对哪些扩展功能比较感兴趣?

欢迎在评论区分享你的想法和经验!

PS:当然也可以在过滤器中实现,但个人觉得注解方式更优雅灵活,你怎么看?