logo 范 · 拾光录
网址收集 关于作者 Github Gitee
杂文随笔5
Hexo博客:基础使用Hexo博客:Next主题Hexo博客:Next进阶使用Hexo博客:Next高级配置基于Node的WIKI管理
前端知识16
HTML常用知识CSS常用知识CSS美化checkbox复选框JavaScript常用知识JavaScript格式化时间戳JavaScript窗口宽高处理JavaScript黑夜主题切换实现方案JavaScript数字转大写简易图片查看器TypeScript基础知识Threejs基础三要素Threejs网格辅助和轨道控制器Threejs物体绘制Electron基础使用Nodejs基础知识animate.css页面动画
Vue框架19
Vite的使用及扩展Vue3父子组件Vue3使用Marked解析MarkdownMermaid图表生成库初始化页面加载动画Axios表单提交二维码解决方案NProgress加载进度条Vue3动态菜单实现Vue3使用ECharts图表Vue3处理Excel导入导出keep-alive页面缓存及setup问题Element:文件上传Element:结合Pinia实现动态菜单Element:图片上传组件Element:自定义统一弹窗组件Element:表格自定义指令控制按钮显示(鉴权)可视化大屏使用缩放适配分辨率
UniApp15
UniApp的基础使用封装网络请求工具及文件上传uni-app的开发记录微信小程序分享原生文件上传Pinia取消滚动条(兼容小程序)tabbar消息数量显示scroll-view上滑到底部加载数据状态栏高度动态设配数据共享与传递uview-plus导航栏实现背景融合Wot UIWot UI实现顶部背景图融合uni-app x
Java基础知识10
基础知识面向对象Lambda表达式常用API常用知识积累try-with-resource注解反射多线程经纬度距离计算
SpringBoot31
application配置Maven创建聚合项目全局异常处理锁机制项目启动初始化数据方式邮件功能集成原生定时任务异步集成阿里云OSS阿里OSS预签名上传基于hutool读excelJSR303WebSocketWebSocket版AI接口流式调用Smart-Doc接口文档生成器application配置信息加密雪花算法工具AOP实现请求参数脱敏思路JWT生成Token及工具类SpringBoot默认JSON与对象转换若依框架:安装使用若依框架:优化和调整文件上传若依框架:管理后台页面优化若依框架:后端接口代码优化SpringAISpringBoot实现AI接口流式调用服务启动时创建MySQL连接自建项目工程树形结构处理工具微信支付代码微信手机号登录
SpringMVC14
跨域处理拦截器RESTful风格伪前后端分离Jackson转换器调整Thymeleaf基于拦截器做权限校验AOP打印接口请求响应日志AOP打印接口请求响应耗时文件上传和回显POST请求加解密实现(AES)POST请求加解密实现(RSA+AES)参数动态校验实现方案真实IP和归属地
MyBatis8
MyBatis基本使用与配置Mapper使用相关MaBatis多数据源配置MyBatisPlus数据统计类处理方案MyBatisPlus条件查询正向工程的实现(H2)mybatis-plus-join
SpringCloud15
Netflix:微服务与搭建Netflix:服务的消费与提供Netflix:EurekaNetflix:ActuatorNetflix:RibbonNetflix:FeignNetflix:HystrixNetflix:ZuulAlibaba:简介与搭建Alibaba:Nacos注册中心Alibaba:RibbonAlibaba:OpenFeignAlibaba:Nacos配置中心Alibaba:GetewayAlibaba:Sentinel
MySQL6
MySQL基础知识MySQL多表查询与事务MySQL常用函数及解决方案MySQL视图MySQL索引安装MySQL
Redis7
Redis介绍和安装Redis配置文件Redis持久化Redis集群Redis语法基础Redis相关问题及解决方案SpringBoot集成Redis使用记录
MongoDB10
Linux安装MongoDBMongoDB基础语法MongoTemplate及SpringBoot配置MongoTemplate中Update操作MongoTemplate中聚合查询MongoTemplate日期归档示例项目使用相关知识归纳地理位置存储与距离查询MongoDB副本集与事务获取类名和属性名工具类
其他数据库1
H2数据库
Python编程6
Python基础知识Python语法yolo目标检测OpenCV的使用及树莓派平台condauv
工具集合13
IDEAMavenGradleGitNginx安装Nginx配置VSCodeJMeter压测DockerOllamaRustFSPicGoObs录制
Linux知识11
Linux常用命令Jar启动脚本VirtualBox安装CentOSVirtualBox安装Ubuntu树莓派安装及使用frp内网穿透ArchLinux:基础系统安装ArchLInux:图形化界面安装ArchLinux:常用软件ArchLinux:深度优化ArchLinux:Niri
创意设计2
Blender:入门知识UI设计基础知识
AI相关9
Claude CodeHermes AgentOpenAI基本使用OpenAI工具调用OpenAI记忆管理OpenAI推理执行OpenAI开发框架Langchainllama.cpp

Nginx配置

如果走Nginx,需要配置,具体看本站的Nginx配置文档

文档

IP2REGION官网Github地址,用到的xdb文件可在Github仓库下载

使用示例

import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import lombok.NonNull;
import lombok.extern.slf4j.Slf4j;
import org.lionsoul.ip2region.service.Config;
import org.lionsoul.ip2region.service.Ip2Region;
import org.springframework.web.servlet.HandlerInterceptor;

/**
 * 访问拦截器
 */
@Slf4j
public class VisitInterceptor implements HandlerInterceptor {

    // 全局唯一的 Ip2Region 查询实例
    private static volatile Ip2Region ip2Region;

    @Override
    public boolean preHandle(@NonNull HttpServletRequest request, @NonNull HttpServletResponse response, @NonNull Object handler) {
        // 延迟加载:第一次有请求时,才去初始化 ip2Region
        if (ip2Region == null) {
            synchronized (VisitInterceptor.class) {
                if (ip2Region == null) {
                    initIp2Region();
                }
            }
        }

        System.out.println("拦截器:" + request.getRequestURI());
        String realIp = getRealIp(request);
        System.out.println("访问IP:" + realIp);

        // 调用归属地查询
        String region = ipRegion(realIp);
        System.out.println("IP归属地:" + region);

        return true;
    }

    /**
     * 初始化 IP 归属地查询服务(通过 InputStream 读取 resources 下的文件)
     */
    private void initIp2Region() {
        try {
            // 通过当前类的类加载器获取 resources 下文件的输入流
            // 假设你的文件放在 src/main/resources/ip2region/ 目录下
            var v4Is = VisitInterceptor.class.getClassLoader().getResourceAsStream("ip2region/ip2region_v4.xdb");
            var v6Is = VisitInterceptor.class.getClassLoader().getResourceAsStream("ip2region/ip2region_v6.xdb");

            if (v4Is == null || v6Is == null) {
                throw new RuntimeException("在 resources 目录下找不到 ip2region 的 xdb 文件,请检查路径!");
            }

            // 1. 创建 v4 的配置
            final Config v4Config = Config.custom()
                    .setCachePolicy(Config.BufferCache) // 全内存缓存,性能最佳
                    .setXdbInputStream(v4Is)            // 改为使用 InputStream 加载
                    .asV4();

            // 2. 创建 v6 的配置
            final Config v6Config = Config.custom()
                    .setCachePolicy(Config.BufferCache)
                    .setXdbInputStream(v6Is)            // 改为使用 InputStream 加载
                    .asV6();

            // 3. 创建 Ip2Region 查询服务
            ip2Region = Ip2Region.create(v4Config, v6Config);
            System.out.println("IP归属地查询服务初始化成功!");

        } catch (Exception e) {
            System.err.println("IP归属地查询服务初始化失败:" + e.getMessage());
            log.error("IP归属地查询服务初始化失败:", e);
        }
    }

    private static String getRealIp(HttpServletRequest request) {
        // 1. 优先从 X-Forwarded-For 获取(应对多层代理)
        String ip = request.getHeader("X-Forwarded-For");
        if (ip != null && !ip.isEmpty() && !"unknown".equalsIgnoreCase(ip)) {
            // 多次反向代理后会有多个IP值,第一个为真实客户端IP
            int index = ip.indexOf(',');
            if (index != -1) {
                return ip.substring(0, index).trim();
            } else {
                return ip;
            }
        }

        // 2. 其次从 X-Real-IP 获取
        ip = request.getHeader("X-Real-IP");
        if (ip != null && !ip.isEmpty() && !"unknown".equalsIgnoreCase(ip)) {
            return ip;
        }

        // 3. 兜底方案:直接获取远程地址
        return request.getRemoteAddr();
    }

    /**
     * 根据 IP 获取归属地
     */
    private static String ipRegion(String ip) {
        // 1. 防御性编程:如果初始化失败或服务未启动,直接返回未知
        if (ip2Region == null) {
            return "未知";
        }
        // 2. 过滤本地回环地址和内网地址
        if ("127.0.0.1".equals(ip) || "0:0:0:0:0:0:0:1".equals(ip) || ip.startsWith("192.168")) {
            return "内网IP";
        }

        try {
            // 3. 执行查询
            String region = ip2Region.search(ip);

            // 4. 提取核心归属地信息
            if (region != null && !region.isEmpty()) {
                String[] parts = region.split("\\|");
                // parts[2] 是省份,parts[3] 是城市
                String province = (parts.length > 2 && !"0".equals(parts[2])) ? parts[2] : "";
                String city = (parts.length > 3 && !"0".equals(parts[3])) ? parts[3] : "";

                // 拼接返回,例如 "贵州省贵阳市"
                return province + city;
            }
        } catch (Exception e) {
            System.err.println("查询IP归属地异常,IP: " + ip + ",原因: " + e.getMessage());
        }
        return "未知";
    }
}
Nginx配置
文档
使用示例