用go gui 框架wails 包装现有的web项目
上头要求要对已有项目的接口数据进行加密,非对称加密,主要的难点在于,怎么在前端项目中把请求数据进行加密,可能是见识太少了,没想到非常安全的方式,所以想到通过包装web项目的方式实现。
方案确定
技术方案有两种选择
- electron ①
- wails ②
以上两种方式都可以把web项目进行包装,
①在实践的时候需要对现有的web项目依赖包进行重新适配、改造,制作完成的安装包大概在60-70M左右,
②只需要简单的处理下请求的部分,或者不处理就可以直接打包进去,制作完成的安装包,只有20M不到
第一版实践
wails项目的项目结构如下
css
wailsClient
├─api
├─build
├─frontend
│ ├─dist
│ └─....
├─httpProxy
├─main.go
└─wails.json
只需要把前端打包的文件放到dist 的位置,然后执行
arduino
wails build -s // -s 表示不进行前端资源编译
直接把项目放进去打包,启动起来,发现无法所有的接口都无法请求,F12打开看看,发现项目的根路径是,
arduino
http://wails.location.com
如图:
如此肯定请求不到接口的。
wails文档中,资源服务器(AssetServer)配置可以对前端的请求进行处理,如下配置,需要实现一个自定义的http.Handler
yaml
AssetServer: &assetserver.Options{
Assets: assets,
Handler: 自定义http.Handler,
},
文档参考:wails.io/zh-Hans/doc... 动态资产
自定义http.Handler,这里面接收到前端的请求以后,转发到真实的API服务器
go
type FileLoader struct {
http.Handler
}
func NewFileLoader() *FileLoader {
return &FileLoader{}
}
func (h *FileLoader) ServeHTTP(w http.ResponseWriter, r *http.Request) {
// 框架带过来的 content-length 是0 ,ReverseProxy 代理的时候就不转发body ,设置成1
r.ContentLength = 1
u, err := url.Parse("http://xxx.xxx.com") // 这里是真实的API接口地址
if err != nil {
log.Println(err.Error())
return
}
proxy := httputil.ReverseProxy{
Director: func(req *http.Request) {
req.URL.Host = u.Host
req.URL.Scheme = u.Scheme
req.Host = u.Host
body, _ := io.ReadAll(req.Body)
req.ContentLength = int64(len(body))
buff := bytes.NewBuffer(body)
req.Body = io.NopCloser(buff)
},
ErrorLog: log.New(os.Stdout, "ReverseProxy:", log.LstdFlags|log.Lshortfile),
}
proxy.ServeHTTP(w, r)
}
如此,前端的所有请求就不会有问题了,接下来就是在Handler这里面处理加密的事情,省略掉,主要说下包装的事情。
如此啊基本的需求是可以实现了,但是请求发现数据接口多的情况下,有好多请求会被挂起,导致前端响应很慢
查了下原因是浏览器限制的并行请求数的问题,单次只能并行10个请求,刚进来请求的比较多,就太多的挂起。
第二版实现
基本的功能实现了,但是请求是有点慢的,想把所有的POST请求不走浏览器,做一个类似mock的东西,把所有Axios请求拦截,然后调用go方法做请求,然后把响应数据返回。
如此,请求实际上并没有通过浏览器发起,而是通过 模拟的Mock 拦截了请求,通过 Js 与 Go 程序交互,调用 Go 方法,做数据请求。
这种方式需要修改下axios 请求的地方,修改如下
js
const service = axios.create({
baseURL: process.env.VUE_APP_BASE_API, // url = base url + request url
timeout: 1000 * 30, // 请求超时时间设置
adapter:config => {
// 模拟服务,返回mock数据
let postDate={
baseURL:config.baseURL,
data:config.data,
headers:config.headers,
method:config.method,
timeout:config.timeout,
url:config.url,
}
console.log(postDate)
return DoRequest(postDate)
}
})
配置一个 adapter ,拦截请求,DoRequest 是Go程序暴露出来的一个方法(自己写的),如下,具体的实现可参考wails文档编写。
go
func (a *App) DoRequest(reqInfo AxiosDate) (resposne AxiosReponse) {
baseUrl := "http://xxx.xxx.com"
bs, err := Post(baseUrl+reqInfo.Url, reqInfo.Data, nil, reqInfo.Headers)
if err != nil {
resposne.Status = 400
return
}
resposne.Status = 200
resposne.StatusText = "ok"
resposne.Data = string(bs)
log.Print(reqInfo)
log.Print(resposne)
return resposne
}
如此,所有的接口请求都走了go程序,就绕过了浏览器并发请求数的限制。
下来就是在请求的地方做加解密处理。
总结
如此,把web项目包装起来,请求通过go程序进行加解密,完美实现请求加密的需求。