-
為什么代理
因為本人最近看了一本《DDoS攻擊與防范深度剖析》,認識到了實現反向代理是防范DDoS的一種可行手段。因此本着好奇的心情,大概的了解一下go的http反向代理實現。
-
代理的分類
- 正向代理:隱藏真實的客戶端向服務端請求,服務器服務端是透明。好比如:VPN。如下圖所示
- 反向代理:真正的服務端的ip地址被隱藏。如下圖所示
-
這里只介紹方向代理
- 反向代理的原理圖
-
方向代理原理大概解說
- 從原理圖中,可以看到整個過程是:客戶端原本向正真的服務器發送請求,但是我們調皮的真正服務器不想暴露自己,因此他叫他的兄弟(代理服務器)暴露自己,先接收客戶端發來的請求,並且查看請求是否合法,然后再發送給真正的服務器。
-
雖然原理是那么的簡單。但是,但是,再客戶端連接代理服務再連接真正的服務器的時候,還是有一些難點的
- 客戶端如何調用API接口,是接收代理服務器的,還是真正的服務器的。哈哈哈,如果調用真正的服務器的接口的話,反向代理服務器就沒有任何意義了。因此我們調用的是代理服務器,但是我們有如何調用代理服務器的接口,代理的服務器的接口又有什么的特點呢?我們先來看一下代理服務器的代碼:
package main import ( "log" "net" "net/http" "net/http/httputil" "net/url" ) type Pxy struct{} func (p *Pxy) ServeHTTP(w http.ResponseWriter, r *http.Request) { trueServer := "http://localhost:2003" url, err := url.Parse(trueServer) if err != nil { log.Println(err) return } proxy := httputil.NewSingleHostReverseProxy(url) log.Println(proxy) proxy.ServeHTTP(w, r) } func main() { http.Handle("/", &Pxy{}) log.Fatal(http.ListenAndServe(":2002", nil)) }
是不是看的有點懵圈,啊哈,這么短就能實現?但是使用go就是這么短小精悍。但是問題又來了:這客戶端如何指定特定接口路徑發起請求啊!不用擔心
http.Handle("/",&Pxy{}) //作用:匹配當前目錄下的所有路徑。就好比如:能夠匹配localhost:9090/test的路徑.因此會自動識別客戶端對代理服務器發起的請求
客戶端發起請求的代碼:
angular實現
1、為了減少跨域的問題,我再angular項目中配置了代理 a.在項目中創建proxy.config.json文件,並將如下代碼寫入 { "/api":{ "target":"http://localhost:2002", "secure":false, "pathRewrite":{ "^/api":"" }, "loglevel":"debug" } } b.在package.json文件中的”start“的位置修改代碼 "start": "ng serve --proxy-config proxy.config.json", 2、在任意一個組件的ts文件中寫入如下代碼 goproxy(){ const httpOptions={ headers:new HttpHeaders({'Content-Type': 'application/json'}) }; var api = "/api/PostTest" this.http.post(api, {"Text":"dfadsf","Picture":"dfasdfsda"},httpOptions).subscribe((Response)=>{ console.log("post請求的代理",Response) }) }
在代碼中,我們可以看到,是使用http://localhost:2002/postTest發起對代理服務器的請求。
- 代理服務器接收到客戶端發來的請求,但是他本身不能對客戶端進行響應數據,因此他會通過這個函數將請求發給真正的服務器。
func (p *Pxy) ServeHTTP(w http.ResponseWriter, r *http.Request) { trueServer := "http://202.192.81.4:2003" url, err := url.Parse(trueServer) if err != nil { log.Println(err) return } proxy := httputil.NewSingleHostReverseProxy(url) log.Println(proxy) proxy.ServeHTTP(w, r) }
- 真正服務器代碼
package main import ( "net/http" "log" "io/ioutil" "encoding/json" ) func PostTest(w http.ResponseWriter,r *http.Request){ w.Header().Set("Access-Control-Allow-Origin", "*") //允許訪問所有域 w.Header().Add("Access-Control-Allow-Headers", "Content-Type") //header的類型 w.Header().Set("content-type", "application/json") //返回數據格式是json body,err := ioutil.ReadAll(r.Body) if err!=nil{ log.Println(err) return } var data interface{} json.Unmarshal(body,&data) log.Println(data) } func main() { http.HandleFunc("/PostTest", PostTest) log.Fatal(http.ListenAndServe(":2003", nil)) log.Println("這里還能運行?") }
從代碼中,我們可以看到真正實現PostTest接口的是在這里接收的。這里處理完數據之后,因為真正服務器傲嬌一點,他也不直接將數據發送給客戶端,還是發送給了代理服務器。然后代理服務器在發送給客戶端。
-
這份代碼只是簡單的介紹了完整的客戶端到代理服務器再到真正服務器的流程,並沒有涉及到代理服務器中的黑名單處理、負載均衡的實現,這些會后續補上。
-
總結
總的來說:客戶端訪問的接口是代理服務器的接口,真正服務器的地址,是不會顯示出來的。
-
后續待解決的問題
- 利用反向代理實現負載均衡
- 實現反向代理的內容滲透
- 黑白名單處理
-
參考資料