用go gui 框架wails 包装现有的web项目

上头要求要对已有项目的接口数据进行加密,非对称加密,主要的难点在于,怎么在前端项目中把请求数据进行加密,可能是见识太少了,没想到非常安全的方式,所以想到通过包装web项目的方式实现。

方案确定

技术方案有两种选择

  • electron ①
  • wails ②

以上两种方式都可以把web项目进行包装,

①在实践的时候需要对现有的web项目依赖包进行重新适配、改造,制作完成的安装包大概在60-70M左右,

②只需要简单的处理下请求的部分,或者不处理就可以直接打包进去,制作完成的安装包,只有20M不到

第一版实践

wails项目的项目结构如下

wailsClient
├─api
├─build
├─frontend
│ ├─dist
│ └─....
├─httpProxy
├─main.go
└─wails.json

只需要把前端打包的文件放到dist 的位置,然后执行

wails build -s  // -s 表示不进行前端资源编译

直接把项目放进去打包,启动起来,发现无法所有的接口都无法请求,F12打开看看,发现项目的根路径是,

http://wails.location.com

如图:

如此肯定请求不到接口的。

wails文档中,资源服务器(AssetServer)配置可以对前端的请求进行处理,如下配置,需要实现一个自定义的http.Handler

AssetServer: &assetserver.Options{
    Assets:  assets,
    Handler: 自定义http.Handler,
},

文档参考:wails.io/zh-Hans/doc… 动态资产

自定义http.Handler,这里面接收到前端的请求以后,转发到真实的API服务器

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 请求的地方,修改如下

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文档编写。

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程序进行加解密,完美实现请求加密的需求。