在開發Web服務的時候,我們可能會需要透過客戶端的IP位址,來協助判斷其所在的地區。一般來說,透過HTTP協定獲取客戶端IP的來源有三個地方,一個是Socket連線的IP位址,一個是HTTP請求中的X-Real-IP
標頭欄位,一個是HTTP請求中的X-Forwarded-For
標頭欄位。
為什麼不能直接用Socket連線的IP位址?因為HTTP連線並不是單純地一點對一點,客戶端有可能會透過幾個正向代理伺服器(Proxy Server),經過一些我們架設的反向代理伺服器(Reverse Proxy Server),才連結到我們的Web伺服器。如果Web伺服器中的Web應用程式是直接取用Socket連線的IP位址的話,就只會得到我們自己架設的反向代理伺服器的IP位址。
代理伺服器在轉發HTTP請求時,可以替HTTP請求加上X-Real-IP
標頭欄位,來保留客戶端的真實IP位址資訊,如此一來Web應用程式就可以藉由讀取HTTP請求中的X-Real-IP
標頭欄位來獲取客戶端的真實IP位址。有些代理伺服器,還會將自己的IP添加到X-Forwarded-For
標頭欄位中,形成一個IP鏈,如下:
X-Forwarded-For: client_ip, proxy_1_ip, proxy_2_ip, proxy_3_ip
在實作Rocket的請求守衛時,request::Request
結構實體雖然有提供一些方法可以取得Socket連線的IP位址和X-Real-IP
標頭欄位的IP位址,但由於過於低階,很難直接拿來做實務上的應用。
Client's IP Address Request Guard for Rocket Framework
「Client's IP Address Request Guard for Rocket Framework」是筆者開發出來的套件,提供兩個請求守衛,可以正確取得客戶端的IP位址,或是取得客戶端的真實IP位址。
Crates.io
Cargo.toml
使用方法
rocket_client_addr
這個crate提供的ClientAddr
結構體可以當作請求守衛,能正確取得客戶端的IP位址。如果有代理伺服器,就會取得最後的代理伺服器的IP位址,但會略過本地端的代理伺服器。
以下是一個使用範例:
#[macro_use]
extern crate rocket;
use rocket_client_addr::ClientAddr;
#[get("/ipv4")]
fn ipv4(client_addr: &ClientAddr) -> String {
client_addr.get_ipv4_string().unwrap()
}
#[get("/ipv6")]
fn ipv6(client_addr: &ClientAddr) -> String {
client_addr.get_ipv6_string()
}
#[launch]
fn rocket() -> _ {
rocket::build().mount("/", routes![ipv4]).mount("/", routes![ipv6])
}
rocket_client_addr
這個crate提供的ClientRealAddr
結構體可以當作請求守衛,能正確取得客戶端的真實IP位址。
以下是一個使用範例:
#[macro_use]
extern crate rocket;
use rocket_client_addr::ClientRealAddr;
#[get("/ipv4")]
fn ipv4(client_addr: &ClientRealAddr) -> String {
client_addr.get_ipv4_string().unwrap()
}
#[get("/ipv6")]
fn ipv6(client_addr: &ClientRealAddr) -> String {
client_addr.get_ipv6_string()
}
#[launch]
fn rocket() -> _ {
rocket::build().mount("/", routes![ipv4]).mount("/", routes![ipv6])
}