最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

Java獲取客戶端真實(shí)IP地址經(jīng)典寫法詳解

 更新時(shí)間:2026年04月13日 09:36:46   作者:愛碼少年?00fly.online  
在Web開發(fā)中,獲取客戶端的真實(shí)IP地址是一個(gè)常見需求,本文將分別給出經(jīng)典寫法(基于工具類/條件判斷)和Lambda寫法(基于函數(shù)式接口?+?流式處理)的實(shí)現(xiàn),并進(jìn)行對(duì)比

在Web開發(fā)中,獲取客戶端的真實(shí)IP地址是一個(gè)常見需求。由于客戶端可能經(jīng)過代理、負(fù)載均衡或CDN,request.getRemoteAddr() 往往只能拿到代理服務(wù)器的IP。因此,需要從特定的HTTP頭(如 X-Forwarded-For、Proxy-Client-IP、WL-Proxy-Client-IP)中解析出原始IP。

下面分別給出經(jīng)典寫法(基于工具類/條件判斷)和Lambda寫法(基于函數(shù)式接口 + 流式處理)的實(shí)現(xiàn),并進(jìn)行對(duì)比。

經(jīng)典寫法(傳統(tǒng)工具類)

使用 if-else 鏈和循環(huán)逐層解析,邏輯清晰,易于理解和調(diào)試。

import javax.servlet.http.HttpServletRequest;
public class IpUtils {
    /**
     * 獲取客戶端真實(shí)IP地址
     * @param request HttpServletRequest
     * @return 真實(shí)IP,如果無(wú)法獲取則返回 "unknown"
     */
    public static String getClientIp(HttpServletRequest request) {
        String ip = request.getHeader("X-Forwarded-For");
        if (ip == null || ip.isEmpty() || "unknown".equalsIgnoreCase(ip)) {
            ip = request.getHeader("Proxy-Client-IP");
        }
        if (ip == null || ip.isEmpty() || "unknown".equalsIgnoreCase(ip)) {
            ip = request.getHeader("WL-Proxy-Client-IP");
        }
        if (ip == null || ip.isEmpty() || "unknown".equalsIgnoreCase(ip)) {
            ip = request.getHeader("HTTP_CLIENT_IP");
        }
        if (ip == null || ip.isEmpty() || "unknown".equalsIgnoreCase(ip)) {
            ip = request.getHeader("HTTP_X_FORWARDED_FOR");
        }
        if (ip == null || ip.isEmpty() || "unknown".equalsIgnoreCase(ip)) {
            ip = request.getRemoteAddr();
        }
        // 對(duì)于通過多級(jí)代理的情況,取第一個(gè)非 unknown 的IP
        if (ip != null && ip.contains(",")) {
            ip = ip.split(",")[0].trim();
        }
        return ip;
    }
}

Lambda寫法(Java 8+)

利用 Stream 和 Optional 對(duì)頭名稱列表進(jìn)行鏈?zhǔn)教幚?,代碼更緊湊、函數(shù)式風(fēng)格更明顯。

import javax.servlet.http.HttpServletRequest;
import java.util.Arrays;
import java.util.Optional;
public class IpUtilsLambda {
    private static final String[] HEADERS_TO_TRY = {
        "X-Forwarded-For",
        "Proxy-Client-IP",
        "WL-Proxy-Client-IP",
        "HTTP_CLIENT_IP",
        "HTTP_X_FORWARDED_FOR"
    };
    /**
     * 獲取客戶端真實(shí)IP地址(Lambda風(fēng)格)
     * @param request HttpServletRequest
     * @return 真實(shí)IP,默認(rèn)返回 request.getRemoteAddr()
     */
    public static String getClientIp(HttpServletRequest request) {
        String ip = Arrays.stream(HEADERS_TO_TRY)
                .map(request::getHeader)
                .filter(header -> header != null && !header.isEmpty() && !"unknown".equalsIgnoreCase(header))
                .findFirst()
                .orElseGet(request::getRemoteAddr);
        // 處理多級(jí)代理情況
        if (ip != null && ip.contains(",")) {
            ip = ip.split(",")[0].trim();
        }
        return ip;
    }
}

或者更進(jìn)一步,將分割邏輯也融入流中:

public static String getClientIpAdvanced(HttpServletRequest request) {
    return Arrays.stream(HEADERS_TO_TRY)
            .map(request::getHeader)
            .filter(header -> header != null && !header.isEmpty() && !"unknown".equalsIgnoreCase(header))
            .map(header -> header.contains(",") ? header.split(",")[0].trim() : header)
            .findFirst()
            .orElseGet(request::getRemoteAddr);
}

兩種寫法對(duì)比

對(duì)比維度經(jīng)典寫法Lambda寫法
可讀性直觀,每個(gè)條件分支清晰可見,適合所有Java開發(fā)者代碼緊湊,但對(duì)不熟悉函數(shù)式編程的開發(fā)者有一定閱讀門檻
代碼行數(shù)較多(約20行)較少(約10行)
擴(kuò)展性添加新頭需要增加一個(gè) if 塊只需在 HEADERS_TO_TRY 數(shù)組中增加一個(gè)元素
性能幾乎無(wú)差異,都是 O(n) 遍歷,且只在請(qǐng)求生命周期內(nèi)執(zhí)行一次同樣 O(n),但 Stream 會(huì)引入少量額外開銷(微乎其微)
調(diào)試便利性可在任意 if 處打斷點(diǎn),逐行跟蹤流內(nèi)調(diào)試相對(duì)困難,需借助 peek() 或 IDE 的流調(diào)試插件
錯(cuò)誤處理可針對(duì)每個(gè)頭單獨(dú)處理異?;蛉罩?/td>需要額外在 map 或 filter 中處理,不夠直觀
團(tuán)隊(duì)接受度高,任何Java程序員都能立即理解依賴于團(tuán)隊(duì)對(duì)函數(shù)式編程的熟悉程度

注意事項(xiàng)與最佳實(shí)踐

  • 代理信任X-Forwarded-For 頭可以被偽造,如果應(yīng)用直接暴露在公網(wǎng),不應(yīng)完全信任該頭。通常需要在反向代理(如Nginx)層面配置 proxy_set_header X-Forwarded-For $remote_addr,并限制內(nèi)部網(wǎng)絡(luò)才能傳遞該頭。
  • IP格式:IPv4和IPv6均可能出現(xiàn)在頭中,無(wú)需額外處理,直接取字符串即可。
  • 多級(jí)代理X-Forwarded-For 的值格式為 client, proxy1, proxy2,通常取第一個(gè)(最左側(cè))為真實(shí)客戶端IP。
  • 空值處理:許多代理會(huì)返回 unknown 字符串,需要過濾掉。
  • 性能考慮:獲取IP的操作發(fā)生在每個(gè)請(qǐng)求中,但遍歷幾個(gè)頭字符串的性能開銷可以忽略不計(jì)。
  • Spring Boot集成:如果使用Spring Boot,也可以直接使用內(nèi)置工具類 RequestUtils.getRemoteAddress() 或 ServletWebRequest.getHeaderValues(),但原理與上述相同。

方法補(bǔ)充

下面對(duì)比了兩種Java獲取客戶端IP的實(shí)現(xiàn)方式。經(jīng)典寫法使用for循環(huán)遍歷IP頭信息數(shù)組,逐個(gè)檢查并返回第一個(gè)有效IP;Lambda寫法則利用Stream API,通過鏈?zhǔn)讲僮魍瓿上嗤倪壿?,代碼更簡(jiǎn)潔。兩種方法都考慮了多種代理頭信息(X-Forwarded-For等)和IP格式處理(分割、去空、trim),最終回退到request.getRemoteAddr()。Lambda寫法展現(xiàn)了函數(shù)式編程的優(yōu)勢(shì),但兩者在功能上完全等效。

經(jīng)典寫法

    @Autowired
    HttpServletRequest request;
    
    String[] IP_HEADERS = {"X-Forwarded-For", "X-Real-IP", "Proxy-Client-IP", "WL-Proxy-Client-IP", "HTTP_X_FORWARDED_FOR", "HTTP_X_FORWARDED", "HTTP_X_CLUSTER_CLIENT_IP", "HTTP_CLIENT_IP", "HTTP_FORWARDED_FOR", "HTTP_FORWARDED", "HTTP_VIA", "REMOTE_ADDR"};
  
    private String getClientIpClassic()
    {
        log.info("##### Classic");
        for (String header : IP_HEADERS)
        {
            String ip = request.getHeader(header);
            if (StringUtils.isNotBlank(ip) && !StringUtils.equalsIgnoreCase("unknown", ip))
            {
                log.info("##### ip header: {}", header);
                String[] ips = ip.split(",");
                return ips[0].trim();
            }
        }
        log.info("##### request.getRemoteAddr");
        return request.getRemoteAddr();
    }

Lambda寫法

    private String getClientIpLambda()
    {
        log.info("##### Lambda");
        return Arrays.stream(IP_HEADERS)
            .peek(log::info)
            .map(request::getHeader)
            .filter(ips -> StringUtils.isNotBlank(ips) && !"unknown".equalsIgnoreCase(ips))
            .flatMap(ips -> Arrays.stream(ips.split(",")))
            .filter(StringUtils::isNotBlank)
            .map(String::trim)
            .findFirst()
            .orElse(request.getRemoteAddr());
    }

總結(jié)

  • 經(jīng)典寫法:適合大多數(shù)項(xiàng)目,尤其是維護(hù)性優(yōu)先、團(tuán)隊(duì)成員技術(shù)水平不一的場(chǎng)景。它直觀、易于調(diào)試和擴(kuò)展,沒有額外的學(xué)習(xí)成本。
  • Lambda寫法:適合追求代碼簡(jiǎn)潔、熟悉函數(shù)式編程、且頭名稱固定不變的場(chǎng)景。它減少了樣板代碼,讓意圖更聚焦于“從一系列候選頭中找出第一個(gè)非空的有效值”。

實(shí)際項(xiàng)目中,兩種寫法均可正確工作。如果項(xiàng)目已經(jīng)使用 Java 8+ 并普遍采用 Stream API,Lambda寫法可以提升代碼的表達(dá)力;否則,經(jīng)典寫法更加穩(wěn)妥。

到此這篇關(guān)于Java獲取客戶端真實(shí)IP地址經(jīng)典寫法詳解的文章就介紹到這了,更多相關(guān)Java獲取客戶端IP地址內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • Java并發(fā)編程之詳解ConcurrentHashMap類

    Java并發(fā)編程之詳解ConcurrentHashMap類

    在之前的文章中已經(jīng)為大家介紹了java并發(fā)編程的工具:BlockingQueue接口、ArrayBlockingQueue、DelayQueue、LinkedBlockingQueue、PriorityBlockingQueue、SynchronousQueue、BlockingDeque接口,本文為系列文章第八篇.需要的朋友可以參考下
    2021-06-06
  • Java Web開發(fā)項(xiàng)目中中文亂碼解決方法匯總

    Java Web開發(fā)項(xiàng)目中中文亂碼解決方法匯總

    這篇文章主要為大家詳細(xì)匯總了Java Web開發(fā)項(xiàng)目中中文亂碼的解決方法,分析了5種Java Web中文亂碼情況,感興趣的小伙伴們可以參考一下
    2016-05-05
  • Java?ConcurrentHashMap實(shí)現(xiàn)線程安全的代碼示例

    Java?ConcurrentHashMap實(shí)現(xiàn)線程安全的代碼示例

    眾所周知ConcurrentHashMap是HashMap的多線程版本,HashMap?在并發(fā)操作時(shí)會(huì)有各種問題,而這些問題,只要使用ConcurrentHashMap就可以完美解決了,本文將給詳細(xì)介紹ConcurrentHashMap是如何保證線程安全的
    2023-05-05
  • Java實(shí)現(xiàn)單鏈表SingleLinkedList增刪改查及反轉(zhuǎn) 逆序等

    Java實(shí)現(xiàn)單鏈表SingleLinkedList增刪改查及反轉(zhuǎn) 逆序等

    單鏈表是鏈表的其中一種基本結(jié)構(gòu)。一個(gè)最簡(jiǎn)單的結(jié)點(diǎn)結(jié)構(gòu)如圖所示,它是構(gòu)成單鏈表的基本結(jié)點(diǎn)結(jié)構(gòu)。在結(jié)點(diǎn)中數(shù)據(jù)域用來(lái)存儲(chǔ)數(shù)據(jù)元素,指針域用于指向下一個(gè)具有相同結(jié)構(gòu)的結(jié)點(diǎn)。 因?yàn)橹挥幸粋€(gè)指針結(jié)點(diǎn),稱為單鏈表
    2021-10-10
  • 詳解Java多線程編程中CountDownLatch阻塞線程的方法

    詳解Java多線程編程中CountDownLatch阻塞線程的方法

    在Java中和ReadWriteLock.ReadLock一樣,CountDownLatch的本質(zhì)也是一個(gè)"共享鎖",這里我們就來(lái)詳解Java多線程編程中CountDownLatch阻塞線程的方法:
    2016-07-07
  • Java的Cglib動(dòng)態(tài)代理實(shí)現(xiàn)方式詳解

    Java的Cglib動(dòng)態(tài)代理實(shí)現(xiàn)方式詳解

    這篇文章主要介紹了Java的Cglib動(dòng)態(tài)代理實(shí)現(xiàn)方式詳解,CGLIB是強(qiáng)大的、高性能的代碼生成庫(kù),被廣泛應(yīng)用于AOP框架,它底層使用ASM來(lái)操作字節(jié)碼生成新的類,為對(duì)象引入間接級(jí)別,以控制對(duì)象的訪問,需要的朋友可以參考下
    2023-11-11
  • Java基礎(chǔ)之集合框架詳解

    Java基礎(chǔ)之集合框架詳解

    這篇文章主要介紹了Java基礎(chǔ)之集合框架詳解,文中有非常詳細(xì)的代碼示例,對(duì)正在學(xué)習(xí)java的小伙伴們有非常好的幫助,需要的朋友可以參考下
    2021-04-04
  • SpringBoot_Cache自定義使用SimpleCacheManager方式

    SpringBoot_Cache自定義使用SimpleCacheManager方式

    這篇文章主要介紹了SpringBoot_Cache自定義使用SimpleCacheManager方式,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2023-07-07
  • Java8常用的新特性詳解

    Java8常用的新特性詳解

    這篇文章主要介紹了Java8常用的新特性詳解,文中有非常詳細(xì)的代碼示例,對(duì)正在學(xué)習(xí)Java8新特性的小伙伴們有非常好的幫助,需要的朋友可以參考下
    2021-04-04
  • Spring MVC入門_動(dòng)力節(jié)點(diǎn)Java學(xué)院整理

    Spring MVC入門_動(dòng)力節(jié)點(diǎn)Java學(xué)院整理

    這篇文章主要介紹了Spring MVC入門,小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過來(lái)看看吧
    2017-08-08

最新評(píng)論

河北省| 巨野县| 台北县| 囊谦县| 雅安市| 奉贤区| 台中县| 桂平市| 磴口县| 屏山县| 辛集市| 鹤壁市| 锦屏县| 昔阳县| 类乌齐县| 丽江市| 长汀县| 岚皋县| 邹城市| 西藏| 监利县| 从江县| 哈巴河县| 郸城县| 普宁市| 云霄县| 长宁县| 眉山市| 苗栗县| 贡嘎县| 鸡东县| 通河县| 柳河县| 阜平县| 资中县| 永川市| 云阳县| 沁阳市| 霍邱县| 漳州市| 锡林浩特市|