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。
总结
程序的逻辑如下:
- 如果没有设置
trust proxy或者设置trust proxy为false,获取的是ip就是req.connection.remoteAddress; - 如果设置了
trust proxy,express会生成对应的过滤函数,过滤[代理ip,x-forwarded-for中的ip],返回结果数组的第一个ip; - ips的处理逻辑类似;
express中是如何处理IP的?的更多相关文章
- Express ( MiddleWare/中间件 路由 在 Express 中使用模板引擎 常用API
A fast, un-opinionated, minimalist web framework for Node.js applications. In general, prefer simply ...
- 为什么express中打开服务端只用listen即可
为什么express中打开服务端只用listen即可:http.createServer(app).listen()与app.listen()的区别 写法一: var app = r ...
- Node+Express中请求和响应对象
在用 Express 构建 Web 服务器时,大部分工作都是从请求对象开始,到响应对象终止. url的组成: 协议协议确定如何传输请求.我们主要是处理 http 和 https.其他常见的协议还有 f ...
- 在linux中设置静态ip地址
在linux中设置静态ip地址1.在终端中输入:vi /etc/sysconfig/network-scripts/ifcfg-eth0 2.开始编辑,填写ip地址.子网掩码.网关.DNS等[root ...
- 精华 对express中next函数的一些理解
关于next主要从三点来进行说明: next的作用是什么? 我们应该在何时使用next? next的内部实现机制是什么? Next的作用 我们在定义express中间件函数的时候都会将第三个参 ...
- 在IIS Express中调试时无法读取配置文件 错误
在IIS Express中调试代码时,如果出现"无法读取配置文件"的问题(如图),这种情况是IIS Express的"applicationhost.config&quo ...
- Ubuntu中设置静态IP和DNS
在Ubuntu中设置静态IP共两步:1>设置IP:2>设置DNS1>设置IP 编辑 /etc/network/interface文件: sudo vi /etc/n ...
- nodejs+express中设置登录拦截器
在nodejs+express中,采用nodejs后端路由控制用户登录后,为了加强前端的安全性控制,阻止用户通过在浏览器地址栏中输入地址访问后台接口,在app.js中需要加入拦截器进行拦截: /*** ...
- Access数据库导入到SQL Server 2005 Express中
安装好SQL Server 2005 Express后,再安装SQL Server Management Studio Express CTP就可以很方便的使用控制台进行数据库的管理.但SQL Ser ...
随机推荐
- QT槽函数获取信号发送对象
Qt 在槽函数中获取信号发送对象 Qt中提供了一个函数 qobject_cast(QObject *object),可以通过这个函数判断信号发出对象 Qt 帮助文档的解释: Returns the g ...
- npm设置为淘宝镜像地址
1. npm设置为淘宝镜像 $npm config set registry https://registry.npm.taobao.org 2. 检查一下 $npm config get regis ...
- mybatis读写分离
mybatis读写分离实现方式有很多种,当然如果没有太过复杂的处理,可以使用阿里云数据库自带的读写分离连接,那样会更加简洁.本文主要对mybatis实现读写分离.主要的实现方式有一下四种: 方案1 通 ...
- JavaScript中bool类型的转化
JavaScript 中 if() 圆括号中的表达式将被看做布尔值来处理.这时就涉及到一些转化的问题. 1.特殊值undefined和null变成false. 2.数字0和NaN变成false. 3. ...
- DOM对象增删元素
<!DOCTYPE html><html lang="en"><head> <meta charset="UTF-8" ...
- Spring Cloud Alibaba基础教程:Nacos 生产级版本 0.8.0
昨晚Nacos社区发布了第一个生产级版本:0.8.0.由于该版本除了Bug修复之外,还提供了几个生产管理非常重要的特性,所以觉得还是有必要写一篇讲讲这次升级,在后续的文章中也都将以0.8.0版本为基础 ...
- Flutter轮播图
前端开发当中最有意思的就是实现动画特效,Flutter提供的各种动画组件可以方便实现各种动画效果.Flutter中的动画组件主要分为两类: 隐式动画控件:只需设置组件开始值,结束值,执行时间,比如An ...
- Sharepoint 编辑WebPart时,WebPart属性为灰色不可用
一般在客户端设置就是灰色的,解决方法就是,要在服务器上进行设置,就可以了.:)
- Pytorch迁移学习实现驾驶场景分类
Pytorch迁移学习实现驾驶场景分类 源代码:https://github.com/Dalaska/scene_clf 1.安装 pytorch 直接用官网上的方法能装上但下载很慢.通过换源安装发现 ...
- Mysql百万级数据查询优化
1. 直接用limit start, count分页语句, 也是我程序中用的方法: select * from product limit start, count当起始页较小时,查询没有性能问题 ...