最近在工作中遇见一个业务场景是获取用户真实的ip地址。就跟现在网上评论展示ip一样的业务场景。然后自己就去了解了一下,
1、pom依赖配置
<!--查询ip归属地-->
<dependency>
<groupId>org.lionsoul</groupId>
<artifactId>ip2region</artifactId>
<version>1.7.2</version>
</dependency>
<dependency>
<groupId>commons-io</groupId>
<artifactId>commons-io</artifactId>
<version>2.5</version>
</dependency>
2、?ip2region.db文件
工具类里面需要使用ip2region.db这个文件。在加载的时候,需要下载仓库中的 ip2region.db 文件,然后放到 resource 目录下
以下文件是文件链接,可自取。
链接:https://pan.baidu.com/s/1PDd_BnpdaOOkYnAc5cB36A?pwd=LA7M? ?
3、工具类
本工具类会将完整的封装方法放入,在使用的时候只需要自己调用即可,非常简单明了。
在工具类里面也会将具体的方法导的具体的包显示出来,便于自己查看溯源。
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.io.FileUtils;
import org.apache.commons.lang3.StringUtils;
import org.lionsoul.ip2region.DataBlock;
import org.lionsoul.ip2region.DbConfig;
import org.lionsoul.ip2region.DbSearcher;
import org.lionsoul.ip2region.Util;
import org.springframework.core.io.Resource;
import org.springframework.core.io.support.PathMatchingResourcePatternResolver;
import org.springframework.core.io.support.ResourcePatternResolver;
import javax.servlet.http.HttpServletRequest;
import java.io.File;
import java.io.InputStream;
import java.lang.reflect.Method;
@Slf4j
public class IpUtil {
public static String getIpAddr(HttpServletRequest request) {
String ip = request.getHeader("X-Real-IP");
if (!StringUtils.isBlank(ip) && !"unknown".equalsIgnoreCase(ip)) {
return ip;
}
ip = request.getHeader("X-Forwarded-For");
if (!StringUtils.isBlank(ip) && !"unknown".equalsIgnoreCase(ip)) {
// 多次反向代理后会有多个IP值,第一个为真实IP。
int index = ip.indexOf(',');
if (index != -1) {
return ip.substring(0, index);
} else {
return ip;
}
} else {
return request.getRemoteAddr();
}
}
public static String getCityInfo(String ip) throws Exception {
//获得文件流时,因为读取的文件是在打好jar文件里面,不能直接通过文件资源路径拿到文件,但是可以在jar包中拿到文件流
ResourcePatternResolver resolver = new PathMatchingResourcePatternResolver();
Resource[] resources = resolver.getResources("ip2region.db");
Resource resource = resources[0];
InputStream is = resource.getInputStream();
File target = new File("ip2region.db");
FileUtils.copyInputStreamToFile(is, target);
is.close();
if (StringUtils.isEmpty(String.valueOf(target))) {
log.error("Error: Invalid ip2region.db file");
return null;
}
DbConfig config = new DbConfig();
DbSearcher searcher = new DbSearcher(config, String.valueOf(target));
//查询算法
//B-tree, B树搜索(更快)
int algorithm = DbSearcher.BTREE_ALGORITHM;
try {
//define the method
Method method;
method = searcher.getClass().getMethod("btreeSearch", String.class);
DataBlock dataBlock;
if (!Util.isIpAddress(ip)) {
log.error("Error: Invalid ip address");
}
dataBlock = (DataBlock) method.invoke(searcher, ip);
String ipInfo = dataBlock.getRegion();
if (!StringUtils.isEmpty(ipInfo)) {
ipInfo = ipInfo.replace("|0", "");
ipInfo = ipInfo.replace("0|", "");
}
return ipInfo;
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
public static String getIpPossession(String ip) throws Exception {
String cityInfo = getCityInfo(ip);
if (!StringUtils.isEmpty(cityInfo)) {
cityInfo = cityInfo.replace("|", " ");
String[] cityList = cityInfo.split(" ");
if (cityList.length > 0) {
// 国内的显示到具体的省
if ("中国".equals(cityList[0])) {
if (cityList.length > 1) {
return cityList[1];
}
}
// 国外显示到国家
return cityList[0];
}
}
return "未知";
}
}
4、方法调用
此时就可以显示正常信息了。
5、问题总结?
?如果显示这个错误的话,说明本地自己写的AOP,joinPoint.getArgs()返回的数组中携带有Request或者Response对象,导致序列化异常。
这个时候我们添加如下就可以4了
Object[] args = point.getArgs();
Object[] arguments = new Object[args.length];
for (int i = 0; i < args.length; i++) {
if (args[i] instanceof ServletRequest || args[i] instanceof ServletResponse || args[i] instanceof MultipartFile) {
//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)
//ServletResponse不能序列化 从入参里排除,否则报异常:java.lang.IllegalStateException: getOutputStream() has already been called for this response
continue;
}
arguments[i] = args[i];
}
String paramter = "";
if (arguments != null) {
try {
paramter = JSONObject.toJSONString(arguments);
} catch (Exception e) {
paramter = arguments.toString();
}
}
log.info("请求接口名:{}", point.getSignature().getName());
log.info("请求参数:{}", paramter);
}
|