需求
用go实现一个反向代理服务。访问redmine系统的地址改为访问go新服务,go服务做代理并改写登录请求参数和返回参数。
原redmine地址:http://192.168.8.134 改为:http://127.0.0.1
分析
使用gin做http服务,使用net/http/httputil包进行代理
实现
package main
import (
"fmt"
"github.com/gin-gonic/gin"
"io/ioutil"
"net/http"
"net/http/httputil"
"net/url"
"strconv"
"strings"
)
func main() {
//创建 gin 引擎
g := gin.Default()
//反向代理所有请求
g.Use(proxyHandler)
// 启动服务
if err := g.Run(":80"); err != nil {
panic(err)
}
}
// api函数
func proxyHandler(c *gin.Context) {
//反向代理的目的地址
var target = "http://192.168.8.134"
proxyURL, _ := url.Parse(target)
reverseProxy := httputil.NewSingleHostReverseProxy(proxyURL)
//req := c.Request
//req.URL.Path = c.Param("path")
originalDirector := reverseProxy.Director
reverseProxy.Director = func(req *http.Request) {
originalDirector(req)
req.Host = proxyURL.Host
req.URL.Scheme = proxyURL.Scheme
req.URL.Host = proxyURL.Host
if req.URL.Path == "/login" {
// 针对登录接口,修改请求参数password对应的值
if req.Method == "POST" && req.Header.Get("Content-Type") == "multipart/form-data" {
err := req.ParseMultipartForm(0)
if err != nil {
panic(err)
}
req.MultipartForm.Value["password"] = []string{"12345678"}
} else if req.Method == "POST" && req.Header.Get("Content-Type") == "application/x-www-form-urlencoded" {
passwd, _ := c.GetPostForm("password")
fmt.Println("========", passwd)
req.Form = c.Request.Form
//修改password为新的值
req.Form.Set("password", "12345678")
newBody := req.Form.Encode()
req.ContentLength = int64(len(newBody))
req.Body = ioutil.NopCloser(strings.NewReader(newBody))
//修改长度
req.Header.Set("Content-Length", strconv.Itoa(len(newBody)))
}
}
}
reverseProxy.ModifyResponse = func(resp *http.Response) error {
resp.ContentLength = -1
resp.Header.Set("Access-Control-Allow-Origin", "*")
//修改重定向的地址
resp.Header.Set("Location", "http://127.0.0.1:8080")
return nil
}
reverseProxy.ServeHTTP(c.Writer, c.Request)
}
效果
原请求地址:
代理后的请求地址
登录成功后:
扩展为动态配置多个反向代理
package main
import (
"fmt"
"github.com/gin-gonic/gin"
"io/ioutil"
"net/http"
"net/http/httputil"
"net/url"
"strconv"
"strings"
)
func main() {
// 创建 gin 引擎
//engine := gin.Default()
//go proxy(8081)
go proxy(8080)
g := gin.Default()
g.Use(proxyHandler)
//g.GET("a", func(context *gin.Context) {
// p := context.Query("port")
// fmt.Println("p", p)
// pInt, _ := strconv.Atoi(p)
// go proxy(pInt)
// context.JSON(http.StatusOK, "ok")
//})
// 启动服务
if err := g.Run(":80"); err != nil {
panic(err)
}
}
func proxy(port int) {
engine := gin.Default()
// router路由
engine.Use(proxyHandler)
// 启动服务
if err := engine.Run(":" + strconv.Itoa(port)); err != nil {
panic(err)
}
}
// api函数
func proxyHandler(c *gin.Context) {
var target = "http://192.168.8.134"
proxyURL, _ := url.Parse(target)
reverseProxy := httputil.NewSingleHostReverseProxy(proxyURL)
//req := c.Request
//req.URL.Path = c.Param("path")
originalDirector := reverseProxy.Director
reverseProxy.Director = func(req *http.Request) {
originalDirector(req)
req.Host = proxyURL.Host
req.URL.Scheme = proxyURL.Scheme
req.URL.Host = proxyURL.Host
fmt.Println("path====", req.URL.Path)
// 修改请求参数
if req.Method == "POST" && req.Header.Get("Content-Type") == "multipart/form-data" {
err := req.ParseMultipartForm(0)
if err != nil {
panic(err)
}
req.MultipartForm.Value["password"] = []string{"12345678"}
} else if req.Method == "POST" && req.Header.Get("Content-Type") == "application/x-www-form-urlencoded" {
pass, _ := c.GetPostForm("password")
fmt.Println("========", pass)
req.Form = c.Request.Form
req.Form.Set("password", "12345678")
//req.Form.Set("utf8", "✓")
//req.Form.Set("back_url", "/")
//req.Form.Set("login", "登录")
//req.Form.Set("authenticity_token", token)
newBody := req.Form.Encode()
req.ContentLength = int64(len(newBody))
req.Body = ioutil.NopCloser(strings.NewReader(newBody))
req.Header.Set("Content-Length", strconv.Itoa(len(newBody)))
}
//b, _ := ioutil.ReadAll(req.Body)
//fmt.Println("body", string(b))
}
reverseProxy.ModifyResponse = func(resp *http.Response) error {
resp.ContentLength = -1
resp.Header.Set("Access-Control-Allow-Origin", "*")
resp.Header.Set("Location", "http://127.0.0.1:8080")
return nil
}
//reverseProxy.ServeHTTP(c.Writer, req)
reverseProxy.ServeHTTP(c.Writer, c.Request)
}
评论区