获取请求IP以及IP解析成省份

某些业务需要获取请求IP以及将IP解析成省份之类的,于是我写了一个工具类,可以直接COPY

/**
 * IP工具类
 * @author xxl
 * @since 2023/11/9
 */
@Slf4j
public class IPUtils {
    /**
     * 过滤本地地址
     */
    public static final String LOCAL_ADDRESS = "127.0.0.1";
    public static final String LOOP_BACK_ADDRESS = "0:0:0:0:0:0:0:1";
    /**
     * 离线查询IP地址的数据文件,这个文件去ip2region GitHub官方仓库获取
     */
    private static  String IP_ADDRESS_FILE_PATH ;

    /**
     * 前从 xdb 文件中加载出来 VectorIndex 数据,然后全局缓存,
     * 每次创建 Searcher 对象的时候使用全局的 VectorIndex 缓存可以减少一次固定的 IO 操作,
     * 从而加速查询,减少 IO 压力。
     */
    private static  byte[] vIndex= null;
    private static Searcher searcher = null;
    
    static {
        try {
            //这个ip2region.xdb我是放在/resources/data/ip2region.xdb目录下的
            String fileName = "/data/ip2region.xdb";
            File existFile = FileUtil.file(FileUtil.getTmpDir() + FileUtil.FILE_SEPARATOR + fileName);
            if(!FileUtil.exist(existFile)) {
                InputStream resourceAsStream = IPUtils.class.getResourceAsStream(fileName);
                FileUtil.writeFromStream(resourceAsStream, existFile);
            }

            IP_ADDRESS_FILE_PATH = existFile.getPath();

            // 从 db 中预先加载 VectorIndex 缓存,并且把这个得到的数据作为全局变量,后续反复使用。
            vIndex = Searcher.loadVectorIndexFromFile(IP_ADDRESS_FILE_PATH);
            // 使用全局的 vIndex 创建带 VectorIndex 缓存的查询对象。
            searcher = Searcher.newWithVectorIndex(IP_ADDRESS_FILE_PATH, vIndex);
        } catch (Exception e) {
            throw new RuntimeException("IPUtils class load error", e);
        }
    }

    /**
     * 每个线程需要单独创建一个独立的 Searcher 对象,但是都共享全局的制度 vIndex 缓存。
     * @param ip IP
     * @return IP地址
     */
    public static String getCity(String ip)  {
        String search = null;
        try {
            search = searcher.search(ip);
        } catch (Exception e) {
            throw new RuntimeException("getCity fail",e);
        }
        return search;
    }

    /**
     * 获取 IP
     *
     * @param request 请求
     * @return 字符串
     */
    public static String getIp(HttpServletRequest request) {
        String ip = null;
        try {
            //解析IP
            ip = new ChainUtils<>(request.getHeader("X-Forwarded-For"))
                //多次反向代理后会有多个ip值,第一个ip才是真实ip
                .chain(re -> StrUtil.isNotBlank(re) ? (re.contains(DOT) ? re.substring(0, re.indexOf(DOT)) : EMPTY) : re)
                //依次查找IP
                .chain(re -> StrUtil.isNotBlank(re) ? re : request.getHeader("X-Real-IP"))
                .chain(re -> StrUtil.isNotBlank(re) ? re : request.getHeader("Proxy-Client-IP"))
                .chain(re -> StrUtil.isNotBlank(re) ? re : request.getHeader("WL-Proxy-Client-IP"))
                .chain(re -> StrUtil.isNotBlank(re) ? re : request.getHeader("HTTP_CLIENT_IP"))
                .chain(re -> StrUtil.isNotBlank(re) ? re : request.getHeader("HTTP_X_FORWARDED_FOR"))
                .chain(re -> StrUtil.isNotBlank(re) ? re : request.getRemoteAddr())
                //过滤本地地址
                .chain(re -> StrUtil.isNotBlank(re) ? (LOOP_BACK_ADDRESS.equals(re) ? LOCAL_ADDRESS : re) : re)
                .getValue(true);
        } catch (Exception e) {
            log.error("getIp fail", e);
        }
        return ip;
    }
}

使用以上工具类需要以下依赖和一个自定义工具类


<dependency>
    <groupId>org.lionsoulgroupId>
    <artifactId>ip2regionartifactId>
    <version>2.7.0version>
dependency>

<dependency>
    <groupId>cn.hutoolgroupId>
    <artifactId>hutool-allartifactId>
    <version>5.8.20version>
dependency>

ChainUtil:为什么写这个工具类在解析请求中的IP参考了https://blog.csdn.net/chwshuang/article/details/71940858此博客中部分代码如下图可以发现有很多if判断,很难看不好维护。于是就写了以下的工具类

/**
 * @author: xxl
 * @since: 2023/11/9
 * @description: 解决if,else地狱
 */
@AllArgsConstructor
public  class ChainUtil<T> {
    /**
     * 存储的值
     */
    private T value;

    public <E> ChainUtil<E> chain(Function<T,E> function) {
        return new ChainUtil<>(function.apply(value));
    }

    /**
     * 获取存储的值
     *
     * @param isNullForException 如果存储的值为null是否抛出异常
     * @return T
     */
    public T getValue(boolean isNullForException) {
        if (isNullForException) {
            Assert.notNull(value, () -> new RuntimeException("chain value is null"));
        }
        return value;
    }
}

if地狱

获取请求IP以及IP解析成省份_第1张图片

你可能感兴趣的:(java,spring,java,ip,spring,boot)