当前位置 博文首页 > Go 实现HTTP中间人代理的操作

    Go 实现HTTP中间人代理的操作

    作者:order_c 时间:2021-05-31 17:53

    goproxy

    Go HTTP(S)代理库, 支持中间人代理解密HTTPS

    项目地址

    安装

    go get github.com/ouqiang/goproxy

    使用

    package main
    import (
        "net/http"
        "time"
        "github.com/ouqiang/goproxy"
    )
    func main() {
        proxy := goproxy.New()
        server := &http.Server{
            Addr:         ":8080",
            Handler:      proxy,
            ReadTimeout:  1 * time.Minute,
            WriteTimeout: 1 * time.Minute,
        }
        err := server.ListenAndServe()
        if err != nil {
            panic(err)
        }
    }

    代理测试

    curl -x localhost:8080 https://www.baidu.com

    中间人代理, 解密HTTPS

    系统需导入根证书 mitm-proxy.crt

    package main
    import (
        "crypto/tls"
        "net/http"
        "sync"
        "time"
        "github.com/ouqiang/goproxy"
    )
    // 实现证书缓存接口
    type Cache struct {
        m sync.Map
    }
    func (c *Cache) Set(host string, cert *tls.Certificate) {
        c.m.Store(host, cert)
    }
    func (c *Cache) Get(host string) *tls.Certificate {
        v, ok := c.m.Load(host)
        if !ok {
            return nil
        }
        return v.(*tls.Certificate)
    }
    func main() {
        proxy := goproxy.New(goproxy.WithDecryptHTTPS(&Cache{}))
        server := &http.Server{
            Addr:         ":8080",
            Handler:      proxy,
            ReadTimeout:  1 * time.Minute,
            WriteTimeout: 1 * time.Minute,
        }
        err := server.ListenAndServe()
        if err != nil {
            panic(err)
        }
    }

    事件处理

    实现Delegate接口

    type Delegate interface {
        // Connect 收到客户端连接
        Connect(ctx *Context, rw http.ResponseWriter)
        // Auth 代理身份认证
        Auth(ctx *Context, rw http.ResponseWriter)
        // BeforeRequest HTTP请求前 设置X-Forwarded-For, 修改Header、Body
        BeforeRequest(ctx *Context)
        // BeforeResponse 响应发送到客户端前, 修改Header、Body、Status Code
        BeforeResponse(ctx *Context, resp *http.Response, err error)
        // ParentProxy 上级代理
        ParentProxy(*http.Request) (*url.URL, error)
        // Finish 本次请求结束
        Finish(ctx *Context)
        // 记录错误信息
        ErrorLog(err error)
    }
    type EventHandler struct{}
    func (e *EventHandler) Connect(ctx *goproxy.Context, rw http.ResponseWriter) {
        // 保存的数据可以在后面的回调方法中获取
        ctx.Data["req_id"] = "uuid"
        // 禁止访问某个域名
        if strings.Contains(ctx.Req.URL.Host, "example.com") {
            rw.WriteHeader(http.StatusForbidden)
            ctx.Abort()
            return
        }
    }
    func (e *EventHandler) Auth(ctx *goproxy.Context, rw http.ResponseWriter)  {
        // 身份验证
    }
    func (e *EventHandler) BeforeRequest(ctx *goproxy.Context) {
        // 修改header
        ctx.Req.Header.Add("X-Request-Id", ctx.Data["req_id"].(string))
        // 设置X-Forwarded-For
        if clientIP, _, err := net.SplitHostPort(ctx.Req.RemoteAddr); err == nil {
            if prior, ok := ctx.Req.Header["X-Forwarded-For"]; ok {
                clientIP = strings.Join(prior, ", ") + ", " + clientIP
            }
            ctx.Req.Header.Set("X-Forwarded-For", clientIP)
        }
        // 读取Body
        body, err := ioutil.ReadAll(ctx.Req.Body)
        if err != nil {
            // 错误处理
            return
        }
        // Request.Body只能读取一次, 读取后必须再放回去
        // Response.Body同理
        ctx.Req.Body = ioutil.NopCloser(bytes.NewReader(body))
    }
    func (e *EventHandler) BeforeResponse(ctx *goproxy.Context, resp *http.Response, err error) {
        if err != nil {
            return
        }
        // 修改response
    }
    // 设置上级代理
    func (e *EventHandler) ParentProxy(req *http.Request) (*url.URL, error) {
        return url.Parse("http://localhost:1087")
    }
    func (e *EventHandler) Finish(ctx *goproxy.Context) {
        fmt.Printf("请求结束 URL:%s\n", ctx.Req.URL)
    }
    // 记录错误日志
    func (e *EventHandler) ErrorLog(err error) {
        log.Println(err)
    }
    func main() {
        proxy := goproxy.New(goproxy.WithDelegate(&EventHandler{}))
        server := &http.Server{
            Addr:         ":8080",
            Handler:      proxy,
            ReadTimeout:  1 * time.Minute,
            WriteTimeout: 1 * time.Minute,
        }
        err := server.ListenAndServe()
        if err != nil {
            panic(err)
        }
    }
    

    以上为个人经验,希望能给大家一个参考,也希望大家多多支持站长博客。如有错误或未考虑完全的地方,望不吝赐教。

    js
    下一篇:没有了