友情提示:本文共有 2734 个字,阅读大概需要 6 分钟。
今天我们要说的这个又是 go 语言中的一个巨坑:在开发环境中运行正常的程序放到生产环境中却报证书错误。说实在的,golang 在跨平台这方面还是做得相当的好的,即使是在 windows 上交叉编译出的 linux 二进制文件,也是可以非常良好的运行的 -- 这实际上也是 go 语言的一大卖点和设计目标。所以这个错误并不太可能是平台之间的差异造成的。仔细查找原因后发现是程序在使用 https/tls 时报的服务器证书不正确。但证书来自大厂的出品,绝对没得问题的,而且也用浏览器测试过了。
这种情况下显然不是服务器的证书有误,其实如果读者有 https/ssl/tls 的原理基础的话很容易就能猜到这是因为本机上没有对应认证机构的根证书。关于 https/ssl/tls 的原理知识大家可在找找看本百家号中的 openssl 相关系统文章,这里就不赘述了。
所以解决的办法也很简单,方法一是将服务器认证的机构证书在本机上装一个就好;二是忽略掉这个证书错误,继续执行,相关的代码如下:
package mainimport ("fmt""net/http""crypto/tls")func main() {tr := &http.Transport{TLSClientConfig: &tls.Config{InsecureSkipVerify: true},}client := &http.Client{Transport: tr}_, err := client.Get("https://xxxx/")if err != nil {fmt.Println(err)}}
重点其实是 TLSClientConfig 那一行,不过这个例子不太完整,一个实际的 https 其实要考虑的内容很多,实际工作中所用的函数要复杂得多,我共享给大伙吧!这段代码随便还解决了本系列上一篇中的 http 超时的问题,相信我,在别的地方您找不到这段代码 :)
//有超时的 http 请求,单位秒func HttpGet_TimeOut(url string, second time.Duration) ([]byte) {defer PrintError("HttpGet_TimeOut"); //var r []byte = nil; var c *http.Client = &http.Client{ Transport: &http.Transport{ TLSClientConfig: &tls.Config{InsecureSkipVerify: true}, //InsecureSkipVerify参数值只能在客户端上设置有效//clq add 让客户端跳过对证书的校验 Dial: func(netw, addr string) (net.Conn, error) { ////c, err := net.DialTimeout(netw, addr, time.Second*3) c, err := net.DialTimeout(netw, addr, time.Second * second); if err != nil { fmt.Println("HttpGet_TimeOut() dail timeout", err); return nil, err; } fmt.Println("HttpGet_TimeOut 连接成功 ..."); //test 仍然有卡死的情况 //clq add 似乎可以在这里设置整个通话过程中的时间,超时 SetConnectTimeOut(c, 10);//test add //c.SetDeadline( //其实这样也可以 return c, nil; }, MaxIdleConnsPerHost: 10, ////ResponseHeaderTimeout: time.Second * 2, ResponseHeaderTimeout: time.Second * second, //这个应该指的是读取头信息时的超时 //IdleConnTimeout: time.Second * second, //test 据说 可以控制连接池中一个连接可以idle多长时间 IdleConnTimeout: time.Second * 60, //test add 据说 可以控制连接池中一个连接可以idle多长时间 }, } //c.Get(url); //resp, err := http.Get(url); fmt.Println("HttpGet_TimeOut c.Get(url) ..."); //test 仍然有卡死的情况 resp, err := c.Get(url); fmt.Println("HttpGet_TimeOut c.Get(url)end ..."); //test 仍然有卡死的情况if err != nil {fmt.Println("error:", err);return nil;} defer resp.Body.Close(); //一定要有 fmt.Println("HttpGet_TimeOut ioutil.ReadAll ..."); //test 仍然有卡死的情况 //resp.Header fmt.Println(resp.Header.Get("Content-Length")); //test fmt.Println(resp.Header.Get("Date")); //test fmt.Println(resp.Header); //test fmt.Println("resp.ContentLength: ", resp.ContentLength); //test 有些 https 站点没有这个头,也许是这个原因导致后面的 ioutil.ReadAll 无效 body, err := ioutil.ReadAll(resp.Body); //就是这里卡的 fmt.Println("HttpGet_TimeOut ioutil.ReadAll end ..."); //test 仍然有卡死的情况 //fmt.Println(string(body)); if err != nil {fmt.Println("error:", err);return nil;} //return r; return body; }//
好了,这可是个超实用的函数,祝大伙周末愉快
本文如果对你有帮助,请点赞收藏《GO语言排雷4:证书有效的情况下https或tls中的报证书错误的解决》,同时在此感谢原作者。