70行node.js代码实现一个类nginx的http代理(且支持身份验证)
先说说背景。
有时我们需要把一个自身不带有权限验证的服务暴露出去,为了提高安全性,我们希望用户访问该服务时,提供一个校验令牌。无令牌就拒绝访问。
所以我决定挑战下自己,写一个能满足上述需求的http代理,基于Node.js和TypeScript。
我们先看看使用效果。代码已经上传到npm仓库。
# 安装
npm i -g @youmoo/lets-proxy
# 运行
PORT=9999 lets-proxy "https://httpbin.org" "hello world"
上述命令先是安装了 @youmoo/lets-proxy 模块,该模块会自动往你的 PATH 中添加一个名为 lets-proxy 的命令;然后执行它。PORT 参数指定监听的端口,第一个参数 https://httpbin.org 指定要代理哪个服务;第二个参数 hello world 是校验码。
既然上面我们代理了 https://httpbin.org 的网站,我们对比一下通过代理访问和通过原网站访问的实际效果。
访问首页:
我们看到本地出现了 Forbidden 提示。这是因为我们没有在浏览器中设置校验码。我们打开左边页面的chrome控制台,加入一个带有 hello world 字样的 cookie。
cookie 设置好后,我们再刷新此页面:
可以看到代理成功了。
httpbin.org 提供了一个获取客户端ip的页面:httpbin.org/ip,我们访问试试:
也是成功的。说明代理是正常的。接下来简单说说代理的原理。
代理的原理
在之前的文章中有介绍过 http 请求格式(回放:说说几种Spring MVC支持的客户端传参方式)。
代理要做的便是,让用户(客户端)不直接访问源网站(本例中是httpbin.og),而是访问代理,代理通过解析用户的请求,以用户的身份将请求几乎原封不动的转发给源网站,然后再将源网站的响应信息回送传给客户端。代理服务器对于客户端和源网站是透明的(二者意识不到代理的存在)。但代理本身其实充当着2种角色:对于客户端来说代理充当了服务器(源网站)的角色;对于源网站而言代理又充当了用户(客户端)的角色。
站在http的角度说,代理就是拦截客户端的 request headers 和 request body ,再将其转发给源网站。然后将源网站的 response headers 和 response body 回写给客户端。
接下来是晾代码的时刻。关键代码不到10行,重要的部分已注释。
#!/usr/bin/env node
import http from "http";
import https from "https";
// 监听的端口
const { PORT = 9999 } = process.env;
// 代理的上游地址和验证码,验证码为空时,表示关闭验证
const [upstream = "https://httpbin.org", auth = null] = process.argv.slice(2);
const server = http.createServer((req, res) => {
const {
method,
url,
headers
} = req;
if (auth) {
const { cookie } = headers;
// 如果开启的验证但cookie中未有此验证信息,报403
if (!cookie || !cookie.includes(auth)) {
res.writeHead(403).end("Forbidden");
return;
}
}
// 组装上游url
const proxied = new URL(upstream + url);
const { protocol, host, pathname, search, hash } = proxied;
headers.host = host;
const _http = protocol === "https:" ? https : http;
// 向上游发出请求
const proxyReq = _http.request({
method,
protocol,
host,
path: pathname,
search,
hash,
// 将客户端的headers也传给上游
headers: headers
}, proxyRes => {
// 将上游返回的headers传给客户端
res.writeHead(proxyRes.statusCode, proxyRes.statusMessage, proxyRes.headers);
// 将上游的response body返给客户端
proxyRes.pipe(res);
})
.on("error", err => {
// 如果请求出错,直接返回500
res.statusCode = 500;
res.statusMessage = err.message;
res.end(err.message);
});
// 将客户端request body传给上游
req.pipe(proxyReq);
});
server.listen(PORT, () => {
console.log("监听:", server.address());
console.log("代理:", upstream);
});
欢迎关注或留言:)
注意,其实nginx本身支持 basic auth 和 auth_request 指令,也能满足文章开头提到的需求。
以上是 70行node.js代码实现一个类nginx的http代理(且支持身份验证) 的全部内容, 来源链接: utcz.com/z/512790.html