1. 程式人生 > >部署在阿里雲 SLB 後面的spring 應用如何獲得使用者真實 IP

部署在阿里雲 SLB 後面的spring 應用如何獲得使用者真實 IP


如果你的應用是部署在阿里雲上面的, 往往在應用的前面會架設一個SLB(負載均衡). 如果 SLB配置成四層轉發, 那麼你的應用看到的 http 連線的對端地址為真實的使用者 IP, `HttpServletRequest.getRemoteAddr()`能返回正確的使用者 IP. 但是如果 SLB 配置成`七層轉發`, `HttpServletRequest.getRemoteAddr()`返回的是 SLB對內地址, 比如: `100.109.*.*`. 在這種情況下如何獲取真實 IP 呢? 其實 SLB 在轉發 http 請求時, 會增加一個 header: `X-FORWARDED-FOR`. 真實 IP 就放在這個頭中. 所一個簡單的獲取真實 IP 的方法就是讀這個頭:
```java
String realIP = request.getHeader("X-FORWARDED-FOR");
```

上面的程式碼假設`X-FORWARDED-FOR` header 中只有一個 IP, 實際情況可能會存在多個 IP, IP之間用逗號隔開. 為什麼會有多個 IP 呢? 因為使用者機器和你的伺服器之間可能存在多個 http 代理或者負載均衡, 每個代理都可能會在`X-FORWARDED-FOR`中加上一個 IP. 另外, 除了獲取真實 IP, 我們有時候也想獲取真實協議(http 或者 https, 比如我們往往在SLB層把 https 請求 變成 http 請求再轉發到後端伺服器, 這樣後端伺服器收到的都是 http 請求). `org.apache.catalina.filters.RemoteIpFilter`就是用來處理這種情況的. 下面程式碼為 spring 下使用 RemoteIpFilter的例子:

```java
@Configuration
public class RemoteIpConfig {
    static final String internalProxyPattern =
      "10\\.\\d{1,3}\\.\\d{1,3}\\.\\d{1,3}|" +
      "192\\.168\\.\\d{1,3}\\.\\d{1,3}|" +
      "169\\.254\\.\\d{1,3}\\.\\d{1,3}|" +
      "127\\.\\d{1,3}\\.\\d{1,3}\\.\\d{1,3}|" +
      "172\\.1[6-9]{1}\\.\\d{1,3}\\.\\d{1,3}|" +
      "172\\.2[0-9]{1}\\.\\d{1,3}\\.\\d{1,3}|" +
      "172\\.3[0-1]{1}\\.\\d{1,3}\\.\\d{1,3}|" +
      "100\\.10[0-9]{1}\\.\\d{1,3}\\.\\d{1,3}";

      @Bean
      public RemoteIpFilter remoteIpFilter() {
        RemoteIpFilter ipFilter = new RemoteIpFilter();
        ipFilter.setProtocolHeader("X-Forwarded-Proto");
        ipFilter.setInternalProxies(internalProxyPattern);
        return ipFilter;
      }
```

其中 internalProxyPattern為內網代理 IP 模式. 前面幾行是 RemoteIpFilter的預設配置, 主要是增加了最後一行, `"100\\.10[0-9]{1}\\.\\d{1,3}\\.\\d{1,3}"`, 這是 阿里雲SLB對內地址段. 實際上整個`100.64.0.0/10`網段都可能會被阿里雲用作SLB 的對內地址, 如果你發現SLB 對內地址不在`"100\\.10[0-9]{1}\\.\\d{1,3}\\.\\d{1,3}"`中, 那麼需要再增加相關的正則表示式.