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 ...
随机推荐
- java中int和Integer的区别?为什么有了int还要有设计Integer?
参考https://blog.csdn.net/chenliguan/article/details/53888018 https://blog.csdn.net/myme95/article/det ...
- SpringMVC 学习笔记(7)spring和springmvc的整合
58. 尚硅谷_佟刚_SpringMVC_Spring整合SpringMVC_解决方案.avi 解决办法让springmvc值扫描@Control控制层和@ControllerAdvice对应的异常处 ...
- 尚学堂 216 java中的字节码操作
所谓的字节码操作就是操作我们已经加载的字节码 接下来我们重点来讲解javaassist类库 使用需要下载jar包,把jar包添加到对应的工程之后 package com.bjsxt.test; pub ...
- C++核心内容和机制
备注:不局限与C++版本 一. 基础知识 数据类型和POD/Trivial 数据类型: 类型转换: NULL和nullptr: 操作符重载: 全局静态变量和成员静态变量的申明和初始化: 左值和右值 ...
- 个人认为目前比较好用的ECMAScript(16-20)新特性
ECMAScript(16.17.18.19)新特性 Array.prototype.includes includes 是数组上的简单实例方法,可以轻松查找到数组中是否包含指定内容(包括NaN) 返 ...
- Spring IoC bean 的加载
前言 本系列全部基于 Spring 5.2.2.BUILD-SNAPSHOT 版本.因为 Spring 整个体系太过于庞大,所以只会进行关键部分的源码解析. 本篇文章主要介绍 Spring IoC 容 ...
- caffe的python接口学习(6)用训练好的模型caffemodel分类新图片
经过前面两篇博文的学习,我们已经训练好了一个caffemodel模型,并生成了一个deploy.prototxt文件,现在我们就利用这两个文件来对一个新的图片进行分类预测. 我们从mnist数据集的t ...
- jQuery制作div板块拖动层排序
html结构: <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www ...
- hive中如何查询除了其中某个字段剩余所有字段
想要将hive分区表中的某个分区复制到新的分区中,使用如下sql语句 insert overwrite table zcfw_sda.sda04_core_request_base_ratio_ifr ...
- Idea自带插件Groovy无法创建和启动
前言 如果现在有人要开始完全重写 Java,那么 Groovy 就像是 Java 2.0.Groovy 并没有取代 Java,而是作为 Java 的补充,它提供了更简单.更灵活的语法,可以在运行时动态 ...