好记性不如 烂笔头,趁热记录下,给未来的自己
0 | 前言
项目需求永远是学习技术最好的动力
项目中,有这样一个业务场景,作为一个平台,可以部署用户写的各种应用,比如gradio应用,streamlit应用,也可以嵌入外部网站的url,这些应用在平台上通过嵌入 iframe 去展示。
然后,针对外部网站,有很多临时性的 demo,或者开发者不方便配置网站 https 证书的时候,往往提供的都是基于 http 协议的访问 url,如果父页面是基于 https 的话,在其嵌入 http 协议的 iframe 链接,就会报错而无法访问。
那么,面对这样的场景,我们该如何去实现呢?
1 | 思路
其实顶层 workaround 的思路就是两个方向,怎么做,可以:
- 在 https 的 iframe 里支持 http 的访问?
- 把 http 的访问转换成 https 的?
第一个方向,调研了几天,各种搜索,问GPT,得到的结论是:不行,原因如下:
- 混合内容问题:当一个安全的 HTTPS 页面试图加载非安全的 HTTP 内容时,这种情况被称为"混合内容"。浏览器通常会阻止这种行为,因为它降低了整个页面的安全性。
- 安全风险:HTTP 内容没有加密,易受中间人攻击。如果在 HTTPS 页面中加载 HTTP 内容,攻击者可能利用这个未加密的内容来攻击整个页面,比如通过注入恶意脚本。
- 隐私和完整性:HTTPS 旨在保护用户数据的隐私和完整性。混合内容使得 HTTPS 页面的这些保障部分失效,因为嵌入的 HTTP 内容不受同样的保护。
- 用户信任:用户可能信任一个安全的 HTTPS 页面,如果这个页面包含不安全的内容,这可能误导用户,使他们对整个页面的安全性有错误的理解。
调转方向,研究第二个思路。其实,此时问题可以进一步细化:任意给定一个 http 的链接,如何用 https 来访问?
自然而然,面对和 url 相关的需求,首先想到的就是 nginx 代理(什么,想不到?!那就多看看烂笔头的文章:》)。大概思路如下:
- 搭建一个 nginx 代理服务;
- 提供一个基于 https 的 url,nginx 接收到请求 url;
- 编写 lua 脚本,实现对请求 url 解析,生成原始的 http 请求 url;
- 使用 proxy_pass 将 https 的请求转发给 原始的 http 地址。
需要注意的是,这种方案需要有个前置条件,具备泛域名证书。泛域名证书要钱?来看看这篇文章,申请一个免费的~《网站要上 HTTPS 却没有 SSL/TLS 证书?| 教你一招搞定免费的(泛域名)SSL/TLS 证书》
2 | 实战
2.1 转换规则
要实现https url 转 http url,首先需要设置转换规则。我们知道,url 是由 协议+域名+端口 组成的,比如:
可以看到,一个 url 里会包含字母、数字、符号等等,那么可以设置转换规则如下:
- 数字/字母 -> 数字/字母
- 符号 -> {符号英文} ,比如
. -> _dot_
,: -> _colon_
- 真实 url 转换后的字符串作为 base url 的二级子域名
假设 base url 为 abc.com,那么 http://192.168.0.1:6443 这个 http url 就可以转换为: 192_dot_168_dot_0_dot_1_colon_6443.abc.com
2.2 nginx 关键配置
首先,通过ngx.re.match找出二级子域名,
然后,将值赋给ngx.var.service,
接着,执行proxy方法,在proxy方法里,使用lua的string.gsub函数对字符串做替换操作,生成真实的访问url,
最后,利用proxy_pass 将请求转发出去。
这里展示核心代码:
ini
server {
listen 80;
location / {
set $service '';
set $type '';
rewrite_by_lua '
local host = ngx.var.host
local m = ngx.re.match(host, "(.+).abc.com")
ngx.var.service = m[1]
ngx.exec("@proxy")
';
}
location @proxy {
access_by_lua_block {
local req_service = ngx.var.service
local real_service = string.gsub(req_service, "_dot_", ".")
real_service = string.gsub(real_service, "_slash_", "/")
real_service = string.gsub(real_service, "_colon_", ":")
ngx.var.service = real_service
}
proxy_pass http://$service;
}
}
需要注意的是,这里只是最核心的部分代码,还有一些必要的nginx配置(比如加头,支持ws协议等等),需要根据业务需求定制化去增加和修改。
以上。