springboot结合ip2region

1.需求

产品要求:按用户ip自动获取城市。

2.解决

在网上找了挺久的,找到一个是线上版本和离线版本

1.线上版本

线上链接
还有离线版本经过技术主管的选型决定使用离线版本,因为跟我们的实际情况贴近。

2.离线版本开源项目ip2region

1.导入Maven包

GitHub地址:https://github.com/lionsoul2014/ip2region
它有分新版跟旧版不建议旧版因为有坑,我已经踩过建议喜新厌旧。

        <dependency>
            <groupId>org.lionsoul</groupId>
            <artifactId>ip2region</artifactId>
            <version>2.7.0</version>
        </dependency>

2.导入ip2region.xdb

注意是放在resources底下
在这里插入图片描述

3.获取IP地址工具类

package io.geekidea.springbootplus.framework.util;

import cn.hutool.core.io.IoUtil;
import cn.hutool.core.util.StrUtil;
import lombok.extern.slf4j.Slf4j;
import org.lionsoul.ip2region.xdb.Searcher;
import org.springframework.core.io.ClassPathResource;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import javax.servlet.http.HttpServletRequest;
import java.io.InputStream;
import java.net.InetAddress;
import java.net.UnknownHostException;

/**
 * 获取IP地址工具类
 * @author geekidea
 * @date 2018-11-08
 */

@Slf4j
public final class IpUtil {

    private static final String UNKNOWN = "unknown";
    private static final String IPV6_LOCAL = "0:0:0:0:0:0:0:1";

    private IpUtil(){
        throw new AssertionError();
    }

    /**
     * 获取请求用户的IP地址
     * @return
     */
    public static String getRequestIp() {
        ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
        HttpServletRequest request = attributes.getRequest();
        return getRequestIp(request);
    }

    /**
     * 获取请求用户的IP地址
     * @param request
     * @return
     */
    public static String getRequestIp(HttpServletRequest request) {
        String ip = request.getHeader("x-forwarded-for");
        if (ip == null || ip.length() == 0 || UNKNOWN.equalsIgnoreCase(ip)) {
            ip = request.getHeader("Proxy-Client-IP");
            //多次反向代理后会有多个ip值,第一个ip才是真实ip
            int index = ip.indexOf(",");
            if (index != -1) {
                return ip.substring(0, index);
            } else {
                return ip;
            }
        }
        if (ip == null || ip.length() == 0 || UNKNOWN.equalsIgnoreCase(ip)) {
            ip = request.getHeader("WL-Proxy-Client-IP");
        }
        if (ip == null || ip.length() == 0 || UNKNOWN.equalsIgnoreCase(ip)) {
            ip = request.getRemoteAddr();
        }

        if (IPV6_LOCAL.equals(ip)){
            ip = getLocalhostIp();
        }
        return ip;
    }

    public static String getLocalhostIp(){
        try {
            return InetAddress.getLocalHost().getHostAddress();
        } catch (UnknownHostException e) {
        }
        return null;
    }

    /**
     * 获取城市信息(如果后面ip2region.db有更新,https://github.com/lionsoul2014/ip2region)
     *
     * @param ip  ip
     * @return
     */
    public static String getCityInfo(String ip) throws Exception {
//注解的代码运行本地可以,打包就会报错 
//本地运行jar报java.io.FileNotFoundException: file:\C:\Users\Administrator\Desktop\user-system-2.0.jar!\BOOT-INF\lib\user-framework-2.0.jar!\ip2region.xdb (文件名、目录名或卷标语法不正确。)。
//        String dbPath = Objects.requireNonNull(IpUtil.class.getResource("/ip2region.xdb")).getPath();
//        File file = new File(dbPath);
//        if ( file.exists() == false ) {
//            log.error("xdb文件Error: Invalid ip2region.xdb file");
//        }
        // 获取ip库路径
        ClassPathResource classPathResource = new ClassPathResource("ip2region.xdb");
        if (classPathResource.getClassLoader() == null){
            log.error("存储路径发生错误,没有被发现");
            return null;
        }
        InputStream inputStream = classPathResource.getInputStream();
        byte[] bytes = IoUtil.readBytes(inputStream);
        Searcher searcher = Searcher.newWithBuffer(bytes);

        //Searcher searcher = Searcher.newWithFileOnly(dbPath);
        String result = searcher.search(ip);
        if (StrUtil.isEmpty(result)) {
            log.error("获取地理位置异常 {}", ip);
            return "XX-XX-XX";
        }
        searcher.close();
        return result;
    }


}

4.如何修改ip2region.xdb

我的要求是要把里面的省市区跟我的数据库里面的数据进行匹配。
ip2region.xdb文件是有ip.mergetes.txt生成的,所以我们要在txt文件进行修改数据
数据修改完成查看文档有提供一个可视化工具。
在这里插入图片描述
在这里插入图片描述

5.serviceImpl实现类

  @Override
    public ApiResult queryBasicRegionByIP(HttpServletRequest request) throws Exception {
        String requestIp = IpUtil.getRequestIp(request);
        String cityInfo = IpUtil.getCityInfo(requestIp);
        //获取的数据格式 中国|0|北京|北京市|电信
        //下面就是业务代码了,我的是进行数据库匹配获取省市code
        IpInfo ipInfo=new IpInfo();
        }

返回对象

package yunping.com.system.entity;

import lombok.Data;
/**
 * 域名信息.
 *
 * @author
 * @date 2023年5月12日10:18:54
 */
@Data
public class IpInfo {
    /*** 国家 */
    private String country;
    /*** 地区 */
    private String region;
    /*** 省 */
    private String provinceId;
    /*** 省 */
    private String provinceNamee;
    /*** 市 */
    private String cityId;
    /*** 市 */
    private String cityName;
    /*** 运营商 */
    private String isp;

    /*** 获取用户的ip地址 */
    private String requestIp;
}

6.打包故障

有的项目打包,可能会出现空指针问题,原因是会把 ip2region.xdb 编译,这样就容易造成本来 9.6M 的内存变成了 15.2M,所以我们需要加一个插件配置,目的是过滤后不需要转码。注意查看target里面的xdb文件大小

    <build>
        <plugins>
            <plugin>
                <artifactId>maven-resources-plugin</artifactId>
                <configuration>
                    <nonFilteredFileExtensions>
                        <nonFilteredFileExtension>xdb</nonFilteredFileExtension>
                    </nonFilteredFileExtensions>
                </configuration>
            </plugin>

        </plugins>

    </build>

7.feign调用服务问题

比如A调用B,B要获取A的信息这个时候A需要弄一个拦截器FeignBasicAuthRequestInterceptor

package com.yunping.framework.core.filter;

import com.yunping.framework.common.constant.AsapConstants;
import com.yunping.framework.util.TraceIdUtils;
import feign.RequestInterceptor;
import feign.RequestTemplate;
import lombok.extern.slf4j.Slf4j;
import org.springframework.util.ObjectUtils;
import org.springframework.web.context.request.RequestAttributes;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import javax.servlet.http.HttpServletRequest;
import java.util.Enumeration;

/**
 * 功能描述: Feign请求拦截器(设置请求头,传递登录信息)
 * @Auther: Solming
 * @Date: 2021/1/13 17:06
* @return: null
 **/
@Slf4j
public class FeignBasicAuthRequestInterceptor implements RequestInterceptor {
  @Override
  public void apply(RequestTemplate requestTemplate) {
    RequestAttributes requestAttributes = RequestContextHolder.getRequestAttributes();
    if (ObjectUtils.isEmpty(requestAttributes)){
      return ;
    }
    ServletRequestAttributes attributes = (ServletRequestAttributes) requestAttributes;
    HttpServletRequest request = attributes.getRequest();
    Enumeration<String> headerNames = request.getHeaderNames();
    if (headerNames != null) {
      while (headerNames.hasMoreElements()) {
        String name = headerNames.nextElement();
        String values = request.getHeader(name);
        // 跳过 content-length
        if ("content-length".equals(name)){
          continue;
        }
        requestTemplate.header(name, values);
      }

    } else {
      log.info("feign interceptor error header:{}", requestTemplate);
    }
  }
}

调用

@FeignClient(
        name = "${feign.url.userCenter}",configuration = FeignSupportConfig.class
)
public interface UserFeignService {
//通过ip地址,获取省市
    @RequestMapping(value = "/api/userCenter/queryBasicRegionByIP", method = RequestMethod.POST,
            consumes = "application/json;charset=UTF-8"
    )
    ApiResult queryBasicRegionByIP();
}

8.关于nginx代理问题。

在匹配的location {}模块中,添加如下参数。

proxy_set_header            Host $host;
proxy_set_header            X-real-ip $remote_addr;
proxy_set_header            X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header            uri  $uri;
proxy_set_header            client_origin  $http_origin;

9.参考资料

https://blog.csdn.net/qq_44138925/article/details/128843196

https://blog.csdn.net/wanglei9876/article/details/82146157

https://my.oschina.net/mdxlcj/blog/1922106

https://blog.csdn.net/weixin_43845227/article/details/111678032

  • 1
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值