首先,我們有多種方式獲取本地IP地址,比如:.net core獲取本地Ip地址的方法 ,這種方式與項目類型無關。
如果后端項目是一個web項目,我們還可以通過HttpContext來獲取后端項目所在服務器的本地IP,而且還能獲取客戶端的IP地址:
var connection = HttpContext.Connection; //客戶端IP地址(v4和v6) var remoteIpAddressIPv4 = connection.RemoteIpAddress?.MapToIPv4()?.ToString(); var remoteIpAddressIPv6 = connection.RemoteIpAddress?.MapToIPv6()?.ToString(); //服務端本地IP地址(v4和v6) var localIpAddressIPv4 = connection.LocalIpAddress?.MapToIPv4()?.ToString(); var localIpAddressIPv6 = connection.LocalIpAddress?.MapToIPv6()?.ToString();
但是現在的web項目應該很少有直接面向客戶端使用的吧,都是中間或多或少有幾個網關或者代理,比如使用nginx:
location / { proxy_pass http://localhost:5000; }
或者前端Vue開發時使用了使用http-proxy-middleware做代理:
devServer: { proxy: { '/api': { target: process.env.VUE_APP_API_BASE_URL, ws: false, changeOrigin: true, pathRewrite: { '^/api': '' // 需要rewrite的, } } } }
那么問題來了,一旦客戶端的請求被代理了,那么上面獲取獲取客戶端IP地址的方式就不准確了,因為請求被代理轉發了,這樣它獲取的就是代理端的IP,而不是客戶端的IP,那么怎么解決這個問題呢?
這就要說到兩個特殊的請求頭了:X-Real-IP 與 X-Forwarded-For
X-Real-IP:表示真實的IP,但是實際上,它表示多級代理中最后一個代理的IP,可惜的是,它只是被一些組織機構認可,貌似並沒有一個標准的規范。
X-Forwarded-For:表示請求從客戶端發起后所經過的所有代理的IP地址,這些IP地址使用英文逗號+空格(, )進行分隔,其中第一個就是客戶端的IP地址,形如:
X-Forwarded-For: 192.168.128.111, 192.168.128.112, 192.168.128.113
那么解決這個問題的關鍵就是在代理轉發請求時,我們需要給轉發的請求加上這個X-Forwarded-For請求頭!X-Real-IP可以添加,也可以不添加,只是后端讀取不一樣。
如果使用的是Nginx:
location / { proxy_pass http://localhost:5000; proxy_http_version 1.1; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; # 如果是代理websocket,還需要下面兩個 # proxy_set_header Upgrade $http_upgrade; # proxy_set_header Connection "upgrade"; }
$remote_addr表示上一級代理的IP,如果上一級是客戶端,那么它就表示客戶端的IP,$proxy_add_x_forwarded_for表示將$remote_addr追加到上一級代理的X-Forwarded-For請求頭中,並將追加后的內容作為X-Forwarded-For請求頭轉發到下一級,這樣客戶端的IP就一級一級的轉發到了服務端
如果是Vue開發過程中使用了http-proxy-middleware做代理:
devServer: { proxy: { '/api': { //如果是API接口 target: process.env.VUE_APP_API_BASE_URL, ws: false, changeOrigin: true, pathRewrite: { '^/api': '' // 需要rewrite的, }, onProxyReq(proxyReq, req, res) { //req中默認是攜帶有x-forwarded-for請求頭的,只需添加X-Real-IP請求頭 req.headers['X-Real-IP'] = req.ip } }, '/socket': { //如果是websocket target: process.env.VUE_APP_SOCKET_BASE_URL, ws: true, changeOrigin: true, onProxyReqWs(proxyReq, req, socket, options, head) { //req中默認是攜帶有x-forwarded-for請求頭的,只需添加X-Real-IP請求頭 req.headers['X-Real-IP'] = req.ip } } } }
這里onProxyReq是代理普通http接口前會觸發的事件,onProxyReqWs則是代理websocket前會觸發的事件
接着后端獲取IP可以從下面的規則中獲取:
string ip = HttpContext.Request.Headers["X-Real-IP"].ToString(); if (string.IsNullOrEmpty(ip)) { string ips = HttpContext.Request.Headers["X-Forwarded-For"].ToString(); ip = ips.Split(new string[] { ", " }, StringSplitOptions.RemoveEmptyEntries).FirstOrDefault(); if (string.IsNullOrEmpty(ip)) { ip = HttpContext.Connection.RemoteIpAddress?.MapToIPv4()?.ToString(); } }