🛡️ 接口级IP白名单配置指南:精准控制接口访问权限
本文介绍了一种基于Spring AOP的接口级IP白名单解决方案,帮助开发者快速实现精细化的接口访问控制。
✨ 核心价值
痛点场景:
- 某些敏感页面需要限制特定IP访问
- 其他普通页面仍需保持正常开放
- 需要动态配置,避免每次修改都重新部署
我们的解决方案: 通过注解+配置的方式,实现接口级别的IP白名单控制,配置热生效,无需重启服务。
🏗️ 架构原理
核心组件
| 组件 | 职责 | 说明 |
|---|---|---|
@RestrictToInternalIp | 标记需要IP控制的接口 | 自定义注解 |
IpAccessAspect | IP校验切面 | AOP实现,核心逻辑 |
ISysConfigService | 配置读取服务 | 动态获取IP白名单 |
sys_config表 | 存储IP白名单配置 | 数据库配置中心 |
工作流程
⚙️ 快速配置
数据库配置
在 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 |
调试技巧
- 开启DEBUG日志查看IP校验过程
- 手动验证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") // 仅工作时间可访问
🛡️ 安全最佳实践
- 定期审计:每月审查一次IP白名单
- 最小权限:只为真正敏感的接口添加保护
- 日志完备:记录所有访问拒绝事件
- 配置备份:备份重要的IP白名单配置
- 监控告警:对异常访问模式设置告警
💬 交流时间
🤔 大家觉得这种方案怎么样?
- 你们项目中是如何处理IP访问控制的?
- 有没有更好的实现思路或踩坑经验?
- 对哪些扩展功能比较感兴趣?
欢迎在评论区分享你的想法和经验!
PS:当然也可以在过滤器中实现,但个人觉得注解方式更优雅灵活,你怎么看?