简介
我们的 Web
服务,往往需要获取用户的真实 IP
,譬如防刷、API
限流等等场景。
每一个 TCP
连接都有 remoteAddress
属性,通过它可以直接获取到请求的 IP
地址。
而在 HTTP
请求中,我们可以通过 request.socket.remoteAddress
访问到这个属性
一般来说我们的应用服务都不会直接接收外部的请求,而会将服务部署在接入层之后,从而实现多台机器的负载均衡和服务的平滑发布,保证高可用。如阿里云 SLB
或 Nginx
反向代理。
此时,我们通过 remoteAddress
获取到的就是代理服务器的 IP
而不是用户的真实 IP
。
这是时候可以通过 X-Forwarded-For
请求头获取
所有的反向代理都实现一个统一的约定,在转发请求给下游服务之前,把请求代理的 IP
地址写入到 X-Forwarded-For
头中,形成了一个 IP
地址列X-Forwarded-For: client, proxy1, proxy2
实现
- 在
utils/
目录下新建ip.js
文件
function getClientIP(req) {
let ip= req.headers['x-forwarded-for'] || // 判断是否有反向代理 IP
req.ip ||
req.connection.remoteAddress || // 判断 connection 的远程 IP
req.socket.remoteAddress || // 判断后端的 socket 的 IP
req.connection.socket.remoteAddress || ''
if(ip) {
ip = ip.replace('::ffff:', '')
}
return ip;
}
module.exports = { getClientIP }
- 代码中使用
const { getClientIP } = require('../utils/ip')
router.get('/test', async ctx => {
const ip = getClientIP(ctx)
ctx.success({ ip })
})
问题
用户如果在请求中直接加入 X-Forwarded-For
的请求头,就可以伪装请求的 IP
。
这种情况需要确定我们的部署架构上到底有多少个反向代理服务,从而在从 X-Forwarded-For
请求头中获取请求的真实 IP
时,过滤掉用户伪造的 IP
地址
// 伪代码
// [ illegalIp, clientRealIp, proxyIp1, proxyIp2 ...]
const val = ctx.get('X-Forwarded-For');
let ips = val ? val.split(/\s*,\s*/) : [];
ips = ips.slice(-(maxProxyCount + 1));
Egg.js
直接内置提供了对应的解决方案
- 修改
config/config.default.js
config.proxy = true;
// 这里配置的就是经过了多少个反向代理服务
config.maxProxyCount = 1;
- 在代码中获取信息
ctx.ip // 获取用户的 IP 地址
ctx.host // 获取用户请求的域名
ctx.protocol // 获取用户请求的协议
发表评论