express中是如何處理IP的?


express獲取client_ip

req.ip // 獲取客戶端ip
req.ips // 獲取請求經過的客戶端與代理服務器的Ip列表

查看源碼

定義獲取ip的入口,

// 源碼 request.js  
defineGetter(req, 'ip', function ip(){
  var trust = this.app.get('trust proxy fn');
  let add = proxyaddr(this, trust);
  return add
});
defineGetter(req, 'ips', function ips() {
  var trust = this.app.get('trust proxy fn');
  var addrs = proxyaddr.all(this, trust);
  addrs.reverse().pop()
  return addrs
});

defineGetter

Object.defineProperty的封裝,所以我們能在req對象上獲取到ip。

trust proxy 的意義

trust proxy :當用戶設置trust proxy的值代表用戶認為這些值是代理服務器。那么在獲取真實客戶端ip的時候就需要進行將這些代理ip過濾掉,
而this.app.get('trust proxy fn')會生成對應的過濾器,這里的過濾器就是函數,類似於數組的filter的callback。

如果用戶沒有設置'trust proxy'this.app.get('trust proxy fn')的返回值如下:

function trustNone () {
  return false
}

如果用戶沒有設置'trust proxy',返回值類似如下:

function trust (addr) {
    if (!isip(addr)) return false
    var ip = parseip(addr)
    var kind = ip.kind()
    if (kind !== subnetkind) {
      if (subnetisipv4 && !ip.isIPv4MappedAddress()) {
        // Incompatible IP addresses
        return false
      }
      // Convert IP to match subnet IP kind
      ip = subnetisipv4
        ? ip.toIPv4Address()
        : ip.toIPv4MappedAddress()
    }
    return ip.match(subnetip, subnetrange)
}

這里牽扯到express中的get set方法,源碼一並列出:

// 源碼 application.js

// 初始化express的是后運行,默認設置trust proxy 為false, 
this.set('trust proxy', false);  

// 定義get方法
methods.forEach(function(method){
  app[method] = function(path){
    if (method === 'get' && arguments.length === 1) {
      return this.set(path); //  調用get運行到這里,相當於調用了set方法
    }
		......
  };
});
// 定義set方法
app.set = function set(setting, val) {
  if (arguments.length === 1) {
    return this.settings[setting];  //調用set方法運行到這里,相當於返回了settings這個對象中的某個配置
  }
  .....
  this.settings[setting] = val;
  ......
  case 'trust proxy':
  	this.set('trust proxy fn', compileTrust(val));
  ....
};

經過以上代碼的運行,

則兩個方法的作用是用來過濾

proxyaddr

這是npm包proxy-addr,express文檔中提到的linklocal,loopback,uniquelocal是在這里定義的,上面提到的ip過濾方法也是這個包來生成。

由函數forward來處理獲取ip,事實上使用x-forwarded-for 和 req.connection.remoteAddress的類獲取ip。

function forwarded (req) {
  ....
  var proxyAddrs = parse(req.headers['x-forwarded-for'] || '')
  var socketAddr = req.connection.remoteAddress
  var addrs = [socketAddr].concat(proxyAddrs)
  return addrs
}

實例

var express = require('express')
var app = express()
app.set('trust proxy','127.0.0.1')
app.use(function (req,res) {
  if(req.url === '/favicon.ico')return
  res.send({
     ip:req.ip,
     ips:req.ips
  })
  console.log(app.get('trust proxy'));
})
app.listen('9090','0.0.0.0')

同時nginx設置代理:

server {
  listen 8062;
  server_name localhost;
  proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
  location / {
    proxy_pass http://127.0.0.1:9090;
  }
}

此時只有一台代理服務,轉發到express的x-forwarded-for為"192.168.1.105",而代理服務器nginx的ip為127.0.0.1,經過forwarded方法處理之后是待過濾的ip數組是 ["127.0.0.1","192.168.1.105"]。

  • 在另一台電腦(內網ip192.168.1.105)訪問192.168.1.102(內網本機IP):8062 ,返回:{"ip":"192.168.1.105","ips":["192.168.1.105"]}

解釋:因為trust proxy和nginx的ip一致,所以過濾掉了nginx的ip,剩下的ip中的第一個被認為是客戶端ip;因為ips本身是就是客戶端ip和代理ip的集合,這里代理nginx被過濾掉了,最后和客戶端一樣。

  • 去掉 app.set('trust proxy','127.0.0.1')的結果:{"ip":"127.0.0.1","ips":[]}

解釋:沒有設置trust proxy,也就是認為沒有代理,獲取的ip為代理服務器的ip。在此情況下ips為為空數組。

  • 換成 app.set('trust proxy','127.0.0.100/28')的結果:{"ip":"127.0.0.1","ips":[]}

解釋:上面的28為網段的CIDR表示法,CIDR是什么呢?有點像正則匹配一樣,舉個例子:127.0.0.1沒有落在127.0.0.100/28所便是的網段,所以express在使用過濾器的時候沒有匹配到,最后返回了第一個ip。沒有匹配到代表ips也為空數組;

  • 換成 app.set('trust proxy','127.0.0.100/18')的結果:{"ip":"192.168.1.105","ips":["192.168.1.105"]}

解釋:127.0.0.1 落在了127.0.0.100/18表示的網段,和上面的相反,過濾掉127.0.0.1。

總結

程序的邏輯如下:

  1. 如果沒有設置trust proxy或者設置trust proxy為false,獲取的是ip就是req.connection.remoteAddress
  2. 如果設置了trust proxy ,express會生成對應的過濾函數,過濾[代理ip,x-forwarded-for中的ip],返回結果數組的第一個ip;
  3. ips的處理邏輯類似;


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM