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的处理逻辑类似;

express中是如何处理IP的?的更多相关文章

  1. Express ( MiddleWare/中间件 路由 在 Express 中使用模板引擎 常用API

    A fast, un-opinionated, minimalist web framework for Node.js applications. In general, prefer simply ...

  2. 为什么express中打开服务端只用listen即可

    为什么express中打开服务端只用listen即可:http.createServer(app).listen()与app.listen()的区别 写法一:        

var app = r ...

  3. Node+Express中请求和响应对象

    在用 Express 构建 Web 服务器时,大部分工作都是从请求对象开始,到响应对象终止. url的组成: 协议协议确定如何传输请求.我们主要是处理 http 和 https.其他常见的协议还有 f ...

  4. 在linux中设置静态ip地址

    在linux中设置静态ip地址1.在终端中输入:vi /etc/sysconfig/network-scripts/ifcfg-eth0 2.开始编辑,填写ip地址.子网掩码.网关.DNS等[root ...

  5. 精华 对express中next函数的一些理解

        关于next主要从三点来进行说明: next的作用是什么? 我们应该在何时使用next? next的内部实现机制是什么? Next的作用 我们在定义express中间件函数的时候都会将第三个参 ...

  6. 在IIS Express中调试时无法读取配置文件 错误

    在IIS Express中调试代码时,如果出现"无法读取配置文件"的问题(如图),这种情况是IIS Express的"applicationhost.config&quo ...

  7. Ubuntu中设置静态IP和DNS

    在Ubuntu中设置静态IP共两步:1>设置IP:2>设置DNS1>设置IP    编辑 /etc/network/interface文件:       sudo vi /etc/n ...

  8. nodejs+express中设置登录拦截器

    在nodejs+express中,采用nodejs后端路由控制用户登录后,为了加强前端的安全性控制,阻止用户通过在浏览器地址栏中输入地址访问后台接口,在app.js中需要加入拦截器进行拦截: /*** ...

  9. Access数据库导入到SQL Server 2005 Express中

    安装好SQL Server 2005 Express后,再安装SQL Server Management Studio Express CTP就可以很方便的使用控制台进行数据库的管理.但SQL Ser ...

随机推荐

  1. QT槽函数获取信号发送对象

    Qt 在槽函数中获取信号发送对象 Qt中提供了一个函数 qobject_cast(QObject *object),可以通过这个函数判断信号发出对象 Qt 帮助文档的解释: Returns the g ...

  2. npm设置为淘宝镜像地址

    1. npm设置为淘宝镜像 $npm config set registry https://registry.npm.taobao.org 2. 检查一下 $npm config get regis ...

  3. mybatis读写分离

    mybatis读写分离实现方式有很多种,当然如果没有太过复杂的处理,可以使用阿里云数据库自带的读写分离连接,那样会更加简洁.本文主要对mybatis实现读写分离.主要的实现方式有一下四种: 方案1 通 ...

  4. JavaScript中bool类型的转化

    JavaScript 中 if() 圆括号中的表达式将被看做布尔值来处理.这时就涉及到一些转化的问题. 1.特殊值undefined和null变成false. 2.数字0和NaN变成false. 3. ...

  5. DOM对象增删元素

    <!DOCTYPE html><html lang="en"><head> <meta charset="UTF-8" ...

  6. Spring Cloud Alibaba基础教程:Nacos 生产级版本 0.8.0

    昨晚Nacos社区发布了第一个生产级版本:0.8.0.由于该版本除了Bug修复之外,还提供了几个生产管理非常重要的特性,所以觉得还是有必要写一篇讲讲这次升级,在后续的文章中也都将以0.8.0版本为基础 ...

  7. Flutter轮播图

    前端开发当中最有意思的就是实现动画特效,Flutter提供的各种动画组件可以方便实现各种动画效果.Flutter中的动画组件主要分为两类: 隐式动画控件:只需设置组件开始值,结束值,执行时间,比如An ...

  8. Sharepoint 编辑WebPart时,WebPart属性为灰色不可用

    一般在客户端设置就是灰色的,解决方法就是,要在服务器上进行设置,就可以了.:)

  9. Pytorch迁移学习实现驾驶场景分类

    Pytorch迁移学习实现驾驶场景分类 源代码:https://github.com/Dalaska/scene_clf 1.安装 pytorch 直接用官网上的方法能装上但下载很慢.通过换源安装发现 ...

  10. Mysql百万级数据查询优化

    1.   直接用limit start, count分页语句, 也是我程序中用的方法: select * from product limit start, count当起始页较小时,查询没有性能问题 ...