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 ...
随机推荐
- Laravel:No application encryption key has been specified.
其实吧,这个就是你没有生成密钥 你首先去看看,如果是刚刚下载的lavavel应该会有一个.env.example文件在根目录下,然后修改这个文件名,改成.env 然后用命令行去执行php artisa ...
- 【原创】强撸基于 .NET 的 Redis Cluster 集群访问组件
Hello 大家好,我是TANZAME,我们又见面了.今天我们来聊聊怎么手撸一个 Redis Cluster 集群客户端,纯手工有干货,您细品. 随着业务增长,线上环境的QPS暴增,自然而然将当前的单 ...
- WeChair项目Alpha冲刺(1/10)
团队项目进行情况 1.昨日进展 因为是Alpha冲刺第一天,所以昨日进展无 2.今日安排 前端:完成前端页面的首页html+css部分 后端:搭建好SpringBoot项目以及完成实体类代码的编 ...
- Blazor带我重玩前端(一)
写在前面 曾经我和前端朋友聊天的时候,我说我希望有一天可以用C#写前端,不过当时更多的是美好的想象,而现在这一切正变得真实…… 什么是Blazor 我们知道浏览器可以正确解释并执行JavaScript ...
- EJB JMS javax.naming.NameNotFoundException: XXX not bound
练习EJB MessageDrivenBean时部署不成功,一直报错 09:57:29,017 WARN [JmsActivation] Failure in jms activation org.j ...
- 黎活明8天快速掌握android视频教程--12_文件的保存与读取
1.当前是把文件保存当前手机的app的data目录下 我们来看看操作保存文件的业务类 package contract.test.savafileapplication; import android ...
- Python三大器之迭代器
Python三大器之迭代器 迭代器协议 迭代器协议规定:对象内部必须提供一个__next__方法,对其执行该方法要么返回迭代器中的下一项(可以暂时理解为下一个元素),要么就引起一个Stopiterat ...
- Java 数组最佳指南,快收藏让它吃灰
两年前,我甚至写过一篇文章,吐槽数组在 Java 中挺鸡肋的,因为有 List 谁用数组啊,现在想想那时候的自己好幼稚,好可笑.因为我只看到了表面现象,实际上呢,List 的内部仍然是通过数组实现的, ...
- 计算机网络之DNS协议
DNS( Domain Name System)是“域名系统”的英文缩写,是一种组织成域层次结构的计算机和网络服务命名系统,它用于TCP/IP网络,它所提供的服务是用来将主机名和域名转换为IP地址的工 ...
- HTML&CSS面试高频考点(二)
HTML&CSS面试高频考点(一) ♥ 6. W3C盒模型与怪异盒模型 标准盒模型(W3C标准) 怪异盒模型(IE标准) 怪异盒模型下盒子的大小=width(content + bord ...