有時候咱們須要獲取Http請求的源IP,但因爲有着各類代理,與反向代理,還有代理請求頭標準的缺失,致使咱們想拿到真正的ip變得更加困難。這篇文章來總結下一個目前可行的比較全面的通用方法。web
首先,真實調用的ip,應該不是內網ip,而且考慮到客戶端多樣性,咱們從通用的Header出發,並也考慮各類常見客戶端的自定義Header。正則表達式
有效ip範圍是,1.0.0.0~255.255.255.255
;這個網上能夠找到不少正則表達式,可是或多或少的有些不全面,例若有的正則表達式認爲"0.0.0.0"也是有效ip。聯想到其實能夠經過數值轉換實現這個判斷:判斷.分割的一共有四個字符串,每一個字符串都是數字而且第一個在1~255
之間,後面三個在0~255
之間,也能夠實現,咱們對比下速度,分別執行JMH程序:apache
正則表達式版本服務器
public static final String IP_SEG = "(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)"; public static final Pattern pattern = Pattern.compile("^(?:" + IP_SEG + "\\.){3}" + IP_SEG + "$"); @Benchmark @BenchmarkMode(Mode.AverageTime) @OutputTimeUnit(TimeUnit.MILLISECONDS) public void testPattern() { for (int i = 0; i < 1000000; i++) { pattern.matcher("").matches(); pattern.matcher("172.14.156.1").matches(); pattern.matcher("x.14.156.1").matches(); pattern.matcher("0.14.156.1").matches(); pattern.matcher("1.14.156.1").matches(); pattern.matcher("001.14.156.1").matches(); pattern.matcher("192.14.156.1145").matches(); } }
分段判斷版本:性能
public static boolean validIp(String ip) { String[] split = ip.split("\\."); if (split.length != 4) { return false; } try { long first = Long.valueOf(split[0]); long second = Long.valueOf(split[1]); long third = Long.valueOf(split[2]); long fourth = Long.valueOf(split[3]); return first < 256 && first > 0 && second < 256 && second >= 0 && third < 256 && third >= 0 && fourth < 256 && fourth >= 0; } catch (NumberFormatException e) { return false; } } @Benchmark @BenchmarkMode(Mode.AverageTime) @OutputTimeUnit(TimeUnit.MILLISECONDS) public void testParse() { for (int i = 0; i < 1000000; i++) { pattern.matcher("").matches(); pattern.matcher("172.14.156.1").matches(); pattern.matcher("x.14.156.1").matches(); pattern.matcher("0.14.156.1").matches(); pattern.matcher("1.14.156.1").matches(); pattern.matcher("001.14.156.1").matches(); pattern.matcher("192.14.156.1145").matches(); } }
正則表達式結果:插件
Benchmark Mode Cnt Score Error Units BenchMark.testParse avgt 5 4184.071 ± 3029.729 ms/op
分段判斷結果:代理
Benchmark Mode Cnt Score Error Units BenchMark.testPattern avgt 5 3814.377 ± 2835.809 ms/op
分段版本在這裏看上去好一些,可是實際其實可能相差不大的。反正證實,這種樸素的分段判斷方法,性能上沒有太大問題。code
在驗證已是有效IP的基礎上,實現判斷是不是內網IP有三種方法:orm
在判斷已是IP的基礎上,這三種方法都會簡化不少,咱們這裏採用分段判斷。blog
可能的Header(HTTP Header不區分大小寫):
x-forwarded-for
:這個是通用的代理Header,通常是逗號分割的多個ip,第一個通常是真實ipx-real-ip
: Nginx代理通常會填寫這個Header,標註真實的ipProxy-Client-IP
和WL- Proxy-Client-IP
:這個通常是通過apache http服務器的請求才會有,用apache http作代理時通常會加上Proxy-Client-IP請求頭,而WL- Proxy-Client-IP是他的weblogic插件加上的頭HTTP_CLIENT_IP
:出自TCP/IP應用協議裏面提到的概念定義的Header,某些代理使用這個Header填寫真實IPHTTP_X_FORWARDED_FOR
:新的HTTP協議中定義的標準x-forwarded-for
,可是比較早出現的沒有這個Header若是上面的Header都沒有,那麼就從RemoteAddress裏面去拿,這是最後的選擇。
/** * 獲取真實ip * * @param request HttpServletRequest * @param acceptInnerIp 是否能夠返回內網ip * @return 真實ip */ public static String getRemoteIpByServletRequest(HttpServletRequest request, boolean acceptInnerIp) { String ip = request.getHeader("x-forwarded-for"); if (StringUtils.isNotBlank(ip)) { // 屢次反向代理後會有多個ip值,第一個ip纔是真實ip if (ip.indexOf(",") != -1) { ip = ip.split(",")[0]; } } if (isIpValid(ip)) { return ip; } ip = request.getHeader("Proxy-Client-IP"); if (isIpValid(ip)) { return ip; } ip = request.getHeader("WL-Proxy-Client-IP"); if (isIpValid(ip)) { return ip; } ip = request.getHeader("HTTP_CLIENT_IP"); if (isIpValid(ip)) { return ip; } ip = request.getHeader("HTTP_X_FORWARDED_FOR"); if (isIpValid(ip)) { return ip; } ip = request.getHeader("X-Real-IP"); if (isIpValid(ip)) { return ip; } ip = request.getRemoteAddr(); return ip; } /** * 判斷是否有效 * @param ip ip * @param acceptInnerIp 是否接受內網ip * @return */ private static boolean isIpValid(String ip, boolean acceptInnerIp) { return acceptInnerIp ? isIpValid(ip) : isIpValidAndNotPrivate(ip); } /** * 僅僅判斷ip是否有效 * @param ip * @return */ private static boolean isIpValid(String ip) { if (StringUtils.isBlank(ip)) { return false; } String[] split = ip.split("\\."); if (split.length != 4) { return false; } try { long first = Long.valueOf(split[0]); long second = Long.valueOf(split[1]); long third = Long.valueOf(split[2]); long fourth = Long.valueOf(split[3]); return first < 256 && first > 0 && second < 256 && second >= 0 && third < 256 && third >= 0 && fourth < 256 && fourth >= 0; } catch (NumberFormatException e) { return false; } } /** * 判斷ip是否有效,而且不是內網ip * @param ip * @return */ private static boolean isIpValidAndNotPrivate(String ip) { if (StringUtils.isBlank(ip)) { return false; } String[] split = ip.split("\\."); try { long first = Long.valueOf(split[0]); long second = Long.valueOf(split[1]); long third = Long.valueOf(split[2]); long fourth = Long.valueOf(split[3]); if (first < 256 && first > 0 && second < 256 && second >= 0 && third < 256 && third >= 0 && fourth < 256 && fourth >= 0) { if (first == 10) { return false; } if (first == 172 && (second >= 16 && second <= 31)) { return false; } if (first == 192 && second == 168) { return false; } return true; } return false; } catch (NumberFormatException e) { return false; } }