概述
因为工作需要协助修改某个golang程序,添加双向认证。但是在调整的过程遇到一个HTTP POST请求变成GET诡异的问题,最后各种搜索,总算解决,博文记录,用于备忘。
代码
服务端
因工作内容,代码有删减,以下样例
package main
import (
"crypto/tls"
"crypto/x509"
"encoding/json"
"fmt"
"io"
"io/ioutil"
"log"
"math/rand"
"mime"
"mime/multipart"
"net/http"
"os"
"strings"
"time"
)
func main() {
mux := http.NewServeMux()
mux.HandleFunc("/...", handleToken)
mux.HandleFunc("/...", handleHandshake)
mux.HandleFunc("/...", handleData)
caCert, err := os.ReadFile("certs/root_cert.pem")
if err != nil {
log.Fatalf("Reading server certificate: %s", err)
}
caCertPool := x509.NewCertPool()
if !caCertPool.AppendCertsFromPEM(caCert) {
fmt.Print("AppendCertsFromPEM failured!!!")
}
// Create TLS configuration with the certificate of the server
tlsConfig := &tls.Config{
ClientCAs: caCertPool, //载入验证客户端证书的根证书
ClientAuth: tls.RequireAndVerifyClientCert, //设置需要客户端证书
}
h1s := &http.Server{
Addr: ":8008",
Handler: mux,
TLSConfig: tlsConfig,
}
log.Fatal(h1s.ListenAndServeTLS("certs/server_cert.pem", "certs/server_key.pem"))
}
...
...
...
func handleToken(w http.ResponseWriter, r *http.Request) {
log.Printf("Token......%s", r.Method)
switch r.Method {
case http.MethodPost:
...
case http.MethodDelete:
...
default:
http.Error(w, "400 Unsupport Method", http.StatusBadRequest)
}
}
func handleHandshake(w http.ResponseWriter, r *http.Request) {
.....
}
客户端
func GetToken(client http.Client) error {
authData := ...
jsonStr, _ := json.Marshal(authData)
resp, err := client.Post(srv+url, "application/json", bytes.NewBuffer(jsonStr))
if err != nil {
log.Printf("Failed get token: err:%s \n", err)
return err
}
defer resp.Body.Close()
....
return nil
}
func main() {
...
client := http.Client{}
// Create a pool with the server certificate since it is not signed
// by a known CA
caCert, err := os.ReadFile("certs/root_cert.pem")
if err != nil {
log.Fatalf("Reading server certificate: %s", err)
}
caCertPool := x509.NewCertPool()
caCertPool.AppendCertsFromPEM(caCert)
clientCert, err := tls.LoadX509KeyPair("certs/client_cert.pem", "certs/client_key.pem")
if err != nil {
panic(err)
}
// Create TLS configuration with the certificate of the server
tlsConfig := &tls.Config{
RootCAs: caCertPool,
Certificates: []tls.Certificate{clientCert},
InsecureSkipVerify: true, //真实证书的情况下,需要删除该行,自签名可以保留
}
client.Transport = &http2.Transport{
TLSClientConfig: tlsConfig}
GetToken(client)
}
Q&A
Q:GetToken发起请求Post请求在服务端收到变成了GET
A:后面经过排查,根源是URL拼接的时候,中间多了一个“/”字符串,原本URL https://127.0.0.1:8008/api/token错误拼接成https://127.0.0.1:8008//api/token,从而在服务器端触发了301响应,go的http client在处理301响应的时候,将POST方法改成GET重新提交,所以导致服务端收到的请求方法是GET而不是POST
对应代码位于client.go
// redirectBehavior describes what should happen when the
// client encounters a 3xx status code from the server.
func redirectBehavior(reqMethod string, resp *Response, ireq *Request) (redirectMethod string, shouldRedirect, includeBody bool) {
switch resp.StatusCode {
case 301, 302, 303:
redirectMethod = reqMethod
shouldRedirect = true
includeBody = false
// RFC 2616 allowed automatic redirection only with GET and
// HEAD requests. RFC 7231 lifts this restriction, but we still
// restrict other methods to GET to maintain compatibility.
// See Issue 18570.
if reqMethod != "GET" && reqMethod != "HEAD" {
redirectMethod = "GET"
}
Q:go run 执行程序,报以下错误
/usr/local/go/pkg/tool/linux_arm64/link: running gcc failed: exit status 1
/usr/bin/ld: 找不到 -l***
collect2: 错误:ld 返回 1
A:因为程序内部通过cgo调用了第三方的动态库,而该动态库又没有在ldconfig或者系统默认目录下,所以找不到对应库导致编译出错,可以通过以下命令临时指定并执行
CGO_LDFLAGS="-L第三方动态库所在路径 -O2 -g" go run test.go