• springboot项目获取真实用户ip(不是虚拟ip)


    最近在工作中遇见一个业务场景是获取用户真实的ip地址。就跟现在网上评论展示ip一样的业务场景。然后自己就去了解了一下,

    1、pom依赖配置

    1. org.lionsoul
    2. ip2region
    3. 1.7.2
    4. commons-io
    5. commons-io
    6. 2.5

    2、 ip2region.db文件

    工具类里面需要使用ip2region.db这个文件。在加载的时候,需要下载仓库中的 ip2region.db 文件,然后放到 resource 目录下

    以下文件是文件链接,可自取。


    链接:https://pan.baidu.com/s/1PDd_BnpdaOOkYnAc5cB36A?pwd=LA7M 
     

    3、工具类

    本工具类会将完整的封装方法放入,在使用的时候只需要自己调用即可,非常简单明了。

    在工具类里面也会将具体的方法导的具体的包显示出来,便于自己查看溯源。

    1. import lombok.extern.slf4j.Slf4j;
    2. import org.apache.commons.io.FileUtils;
    3. import org.apache.commons.lang3.StringUtils;
    4. import org.lionsoul.ip2region.DataBlock;
    5. import org.lionsoul.ip2region.DbConfig;
    6. import org.lionsoul.ip2region.DbSearcher;
    7. import org.lionsoul.ip2region.Util;
    8. import org.springframework.core.io.Resource;
    9. import org.springframework.core.io.support.PathMatchingResourcePatternResolver;
    10. import org.springframework.core.io.support.ResourcePatternResolver;
    11. import javax.servlet.http.HttpServletRequest;
    12. import java.io.File;
    13. import java.io.InputStream;
    14. import java.lang.reflect.Method;
    15. @Slf4j
    16. public class IpUtil {
    17. public static String getIpAddr(HttpServletRequest request) {
    18. String ip = request.getHeader("X-Real-IP");
    19. if (!StringUtils.isBlank(ip) && !"unknown".equalsIgnoreCase(ip)) {
    20. return ip;
    21. }
    22. ip = request.getHeader("X-Forwarded-For");
    23. if (!StringUtils.isBlank(ip) && !"unknown".equalsIgnoreCase(ip)) {
    24. // 多次反向代理后会有多个IP值,第一个为真实IP。
    25. int index = ip.indexOf(',');
    26. if (index != -1) {
    27. return ip.substring(0, index);
    28. } else {
    29. return ip;
    30. }
    31. } else {
    32. return request.getRemoteAddr();
    33. }
    34. }
    35. public static String getCityInfo(String ip) throws Exception {
    36. //获得文件流时,因为读取的文件是在打好jar文件里面,不能直接通过文件资源路径拿到文件,但是可以在jar包中拿到文件流
    37. ResourcePatternResolver resolver = new PathMatchingResourcePatternResolver();
    38. Resource[] resources = resolver.getResources("ip2region.db");
    39. Resource resource = resources[0];
    40. InputStream is = resource.getInputStream();
    41. File target = new File("ip2region.db");
    42. FileUtils.copyInputStreamToFile(is, target);
    43. is.close();
    44. if (StringUtils.isEmpty(String.valueOf(target))) {
    45. log.error("Error: Invalid ip2region.db file");
    46. return null;
    47. }
    48. DbConfig config = new DbConfig();
    49. DbSearcher searcher = new DbSearcher(config, String.valueOf(target));
    50. //查询算法
    51. //B-tree, B树搜索(更快)
    52. int algorithm = DbSearcher.BTREE_ALGORITHM;
    53. try {
    54. //define the method
    55. Method method;
    56. method = searcher.getClass().getMethod("btreeSearch", String.class);
    57. DataBlock dataBlock;
    58. if (!Util.isIpAddress(ip)) {
    59. log.error("Error: Invalid ip address");
    60. }
    61. dataBlock = (DataBlock) method.invoke(searcher, ip);
    62. String ipInfo = dataBlock.getRegion();
    63. if (!StringUtils.isEmpty(ipInfo)) {
    64. ipInfo = ipInfo.replace("|0", "");
    65. ipInfo = ipInfo.replace("0|", "");
    66. }
    67. return ipInfo;
    68. } catch (Exception e) {
    69. e.printStackTrace();
    70. }
    71. return null;
    72. }
    73. public static String getIpPossession(String ip) throws Exception {
    74. String cityInfo = getCityInfo(ip);
    75. if (!StringUtils.isEmpty(cityInfo)) {
    76. cityInfo = cityInfo.replace("|", " ");
    77. String[] cityList = cityInfo.split(" ");
    78. if (cityList.length > 0) {
    79. // 国内的显示到具体的省
    80. if ("中国".equals(cityList[0])) {
    81. if (cityList.length > 1) {
    82. return cityList[1];
    83. }
    84. }
    85. // 国外显示到国家
    86. return cityList[0];
    87. }
    88. }
    89. return "未知";
    90. }
    91. }

    4、方法调用

    此时就可以显示正常信息了。

    5、问题总结 

     如果显示这个错误的话,说明本地自己写的AOP,joinPoint.getArgs()返回的数组中携带有Request或者Response对象,导致序列化异常。

    这个时候我们添加如下就可以4了

    1. Object[] args = point.getArgs();
    2. Object[] arguments = new Object[args.length];
    3. for (int i = 0; i < args.length; i++) {
    4. if (args[i] instanceof ServletRequest || args[i] instanceof ServletResponse || args[i] instanceof MultipartFile) {
    5. //ServletRequest不能序列化,从入参里排除,否则报异常:java.lang.IllegalStateException: It is illegal to call this method if the current request is not in asynchronous mode (i.e. isAsyncStarted() returns false)
    6. //ServletResponse不能序列化 从入参里排除,否则报异常:java.lang.IllegalStateException: getOutputStream() has already been called for this response
    7. continue;
    8. }
    9. arguments[i] = args[i];
    10. }
    11. String paramter = "";
    12. if (arguments != null) {
    13. try {
    14. paramter = JSONObject.toJSONString(arguments);
    15. } catch (Exception e) {
    16. paramter = arguments.toString();
    17. }
    18. }
    19. log.info("请求接口名:{}", point.getSignature().getName());
    20. log.info("请求参数:{}", paramter);
    21. }

  • 相关阅读:
    Android 开源一个USB读写demo,从多个USB设备中选择一个实现外设控制的通信
    CSS读书笔记
    Bootstrap Blazor Table 组件(二)手动刷新组件数据
    Java中如何计算一个HashSet对象的大小呢?
    Openresty(二十)加载lua文件的一些细节
    前端开发中,文本单行或多行溢出使用省略号显示
    第九章、ansible基于roles角色管理大项目
    vscode远程链接下的python环境配置
    我面试的人成了我的领导,我该不该离职?
    cubeIDE开发, stm32的ADC(模数转换器) 开发要点
  • 原文地址:https://blog.csdn.net/ProBaiXiaodi/article/details/126772033