实现http反向代理完整流程(go+angular)
-
为什么代理
因为本人最近看了一本《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接口的是在这里接收的。这里处理完数据之后,因为真正服务器傲娇一点,他也不直接将数据发送给客户端,还是发送给了代理服务器。然后代理服务器在发送给客户端。
-
这份代码只是简单的介绍了完整的客户端到代理服务器再到真正服务器的流程,并没有涉及到代理服务器中的黑名单处理、负载均衡的实现,这些会后续补上。
-
总结
总的来说:客户端访问的接口是代理服务器的接口,真正服务器的地址,是不会显示出来的。
-
后续待解决的问题
- 利用反向代理实现负载均衡
- 实现反向代理的内容渗透
- 黑白名单处理
-
参考资料