1. 使用微信支付时,有的手机ip获取不到外网ip,导致无法支付成功,提示:网络环境未能通过安全验证 请稍后再试
2. 之前使用的方法,只是粗略的,并不能适用所有情况,所有手机。
同时首先确保ngix代理访问api中做了相关配置,如:
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
经过综合多种文章,最终实现了多层穿透代理获得真实ip的实现方法:
3. 代码贴上:
public class IPAddressUtils {
/**
* 获取用户真实IP地址,不使用request.getRemoteAddr()的原因是有可能用户使用了代理软件方式避免真实IP地址,
* 可是,如果通过了多级反向代理的话,X-Forwarded-For的值并不止一个,而是一串IP值
*
* 获取终端ip(高配版)
* @return ip
*/
public static String getRealIpAddr(HttpServletRequest request) {
String ip = request.getHeader("x-forwarded-for");
LOGGER.info("x-forwarded-for ip: {}", ip);
if (ip != null && ip.length() != 0 && !"unknown".equalsIgnoreCase(ip)) {
//可能有代理
if(ip.indexOf(".") == -1) { //没有“.”肯定是非IPv4格式
ip = null;
} else {
if (ip.indexOf(",") != -1) {
//有“,”,估计多个代理。取第一个不是内网的IP。
ip = ip.replace(" ", "").replace("'", "");
String[] temparyip = ip.split(",|;");
for (int i = 0; i < temparyip.length; i++) {
if (isIPAddress(temparyip[i]) && ! isPrivateIp(temparyip[i])) {
return temparyip[i];
}
}
} else if(isIPAddress(ip)) { //代理即是IP格式
return ip;
} else {
ip = null; //代理中的内容 非IP,取IP
}
}
}
if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
ip = request.getHeader("Proxy-Client-IP");
LOGGER.info("Proxy-Client-IP: {}", ip);
}
if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
ip = request.getHeader("WL-Proxy-Client-IP");
LOGGER.info("WL-Proxy-Client-IP: {}", ip);
}
if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
ip = request.getHeader("HTTP_CLIENT_IP");
LOGGER.info("HTTP_CLIENT_IP: {}", ip);
}
if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
ip = request.getHeader("HTTP_X_FORWARDED_FOR");
LOGGER.info("HTTP_X_FORWARDED_FOR: {}", ip);
}
if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
ip = request.getHeader("X-Real-IP");
LOGGER.info("X-Real-IP: {}", ip);
}
if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
ip = request.getRemoteAddr();
LOGGER.info("getRemoteAddr-IP: {}", ip);
}
LOGGER.info("=====获取客户端ip: {}", ip);
return ip;
}
/**
* 是否私有ip地址
*
* 在IPv4中,私有地址的范围分别是:
* A类地址范围:10.0.0.0—10.255.255.255
* B类地址范围:172.16.0.0---172.31.255.555
* C类地址范围:192.168.0.0---192.168.255.255
* @param tmpIp
* @return
*/
public static boolean isPrivateIp(String tmpIp) {
LOGGER.info("isPrivateIp:{}", tmpIp);
if(StringUtils.isBlank(tmpIp)) {
return false;
}
if("10.".equals(tmpIp.substring(0, 3)) || "192.168".equals(tmpIp.substring(0, 7))) {
return true;
}
if("172.".equals(tmpIp.substring(0, 4))) {
String subStr = tmpIp.substring(4, 7);
LOGGER.info("isPrivateIp-subStr:{}", subStr);
Integer subNum = Integer.valueOf(tmpIp.substring(4, 6));
LOGGER.info("isPrivateIp-subNum:{}", subNum);
if(subStr.endsWith(".") && subNum > 15 && subNum < 32) {
return true;
}
}
return false;
}
public static boolean isIPAddress(String str1) {
if(StringUtils.isBlank(str1) || str1.length() < 7 || str1.length() > 15) return false;
String regformat = "^\\d{1,3}\\.\\d{1,3}\\.\\d{1,3}\\.\\d{1,3}$";
return str1.matches(regformat);
}
}