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 nodeimport 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

回到顶部