From c27b32df4cc551e61b7fe1a99f46341d4ac5456e Mon Sep 17 00:00:00 2001 From: Liujian <824010343@qq.com> Date: Sun, 24 Dec 2023 22:02:55 +0800 Subject: [PATCH] =?UTF-8?q?=E6=96=B0=E5=A2=9Ejs=E6=8B=A6=E6=88=AA=E5=99=A8?= =?UTF-8?q?=E3=80=81rsa=E5=8A=A0=E5=AF=86=E6=8F=92=E4=BB=B6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/apinto/plugin.go | 4 + drivers/discovery/nacos/client.go | 1 - drivers/plugins/js-inject/config.go | 25 +++++ drivers/plugins/js-inject/executor.go | 86 +++++++++++++++ drivers/plugins/js-inject/factory.go | 18 +++ drivers/plugins/js-inject/tool.go | 90 +++++++++++++++ drivers/plugins/rsa-filter/config.go | 22 ++++ drivers/plugins/rsa-filter/executor.go | 147 +++++++++++++++++++++++++ drivers/plugins/rsa-filter/factory.go | 18 +++ drivers/plugins/rsa-filter/tool.go | 109 ++++++++++++++++++ drivers/router/listener.go | 3 +- go.mod | 2 + utils/encode.go | 9 +- 13 files changed, 528 insertions(+), 6 deletions(-) create mode 100644 drivers/plugins/js-inject/config.go create mode 100644 drivers/plugins/js-inject/executor.go create mode 100644 drivers/plugins/js-inject/factory.go create mode 100644 drivers/plugins/js-inject/tool.go create mode 100644 drivers/plugins/rsa-filter/config.go create mode 100644 drivers/plugins/rsa-filter/executor.go create mode 100644 drivers/plugins/rsa-filter/factory.go create mode 100644 drivers/plugins/rsa-filter/tool.go diff --git a/app/apinto/plugin.go b/app/apinto/plugin.go index d26cf42c..c86bc485 100644 --- a/app/apinto/plugin.go +++ b/app/apinto/plugin.go @@ -8,6 +8,7 @@ import ( extra_params "github.com/eolinker/apinto/drivers/plugins/extra-params" grpc_proxy_rewrite "github.com/eolinker/apinto/drivers/plugins/grpc-proxy-rewrite" "github.com/eolinker/apinto/drivers/plugins/gzip" + js_inject "github.com/eolinker/apinto/drivers/plugins/js-inject" params_check "github.com/eolinker/apinto/drivers/plugins/params-check" "github.com/eolinker/apinto/drivers/plugins/prometheus" request_file_parse "github.com/eolinker/apinto/drivers/plugins/request-file-parse" @@ -15,6 +16,7 @@ import ( response_file_parse "github.com/eolinker/apinto/drivers/plugins/response-file-parse" response_filter "github.com/eolinker/apinto/drivers/plugins/response-filter" response_rewrite_v2 "github.com/eolinker/apinto/drivers/plugins/response-rewrite_v2" + rsa_filter "github.com/eolinker/apinto/drivers/plugins/rsa-filter" access_log "github.com/eolinker/apinto/drivers/plugins/access-log" body_check "github.com/eolinker/apinto/drivers/plugins/body-check" @@ -88,6 +90,8 @@ func pluginRegister(extenderRegister eosc.IExtenderDriverRegister) { cors.Register(extenderRegister) circuit_breaker.Register(extenderRegister) app.Register(extenderRegister) + rsa_filter.Register(extenderRegister) + js_inject.Register(extenderRegister) // 可观测性(输出内容到第三方) access_log.Register(extenderRegister) diff --git a/drivers/discovery/nacos/client.go b/drivers/discovery/nacos/client.go index 6bf4057f..4ee088e8 100644 --- a/drivers/discovery/nacos/client.go +++ b/drivers/discovery/nacos/client.go @@ -95,7 +95,6 @@ func newClient(name string, address []string, params map[string]string) (*client func (c *client) GetNodeList(serviceName string) ([]discovery.NodeInfo, error) { nodes := make([]discovery.NodeInfo, 0) set := make(map[string]struct{}) - instances, err := c.namingClient.SelectInstances(vo.SelectInstancesParam{ ServiceName: serviceName, Clusters: c.clusters, diff --git a/drivers/plugins/js-inject/config.go b/drivers/plugins/js-inject/config.go new file mode 100644 index 00000000..4fb457d6 --- /dev/null +++ b/drivers/plugins/js-inject/config.go @@ -0,0 +1,25 @@ +package js_inject + +import ( + "github.com/eolinker/apinto/drivers" + "github.com/eolinker/eosc" +) + +type Config struct { + Variables []Variable `json:"variables" label:"变量列表"` + InjectCode string `json:"inject" label:"注入代码"` + MatchContentType []string `json:"match_content_type" label:"匹配的Content-Type"` +} + +type Variable struct { + Key string `json:"key" label:"变量名"` + Value string `json:"value" label:"变量值"` +} + +func Create(id, name string, conf *Config, workers map[eosc.RequireId]eosc.IWorker) (eosc.IWorker, error) { + e := &executor{ + WorkerBase: drivers.Worker(id, name), + } + e.reset(conf) + return e, nil +} diff --git a/drivers/plugins/js-inject/executor.go b/drivers/plugins/js-inject/executor.go new file mode 100644 index 00000000..75f21294 --- /dev/null +++ b/drivers/plugins/js-inject/executor.go @@ -0,0 +1,86 @@ +package js_inject + +import ( + "fmt" + "strings" + + "github.com/eolinker/apinto/utils" + + "github.com/eolinker/eosc/log" + + "github.com/eolinker/apinto/drivers" + "github.com/eolinker/eosc" + "github.com/eolinker/eosc/eocontext" + http_service "github.com/eolinker/eosc/eocontext/http-context" +) + +var _ eocontext.IFilter = (*executor)(nil) +var _ http_service.HttpFilter = (*executor)(nil) +var _ eosc.IWorker = (*executor)(nil) + +type executor struct { + drivers.WorkerBase + injectCode string + matchContentType []string +} + +func (e *executor) DoFilter(ctx eocontext.EoContext, next eocontext.IChain) (err error) { + return http_service.DoHttpFilter(e, ctx, next) +} + +func (e *executor) DoHttpFilter(ctx http_service.IHttpContext, next eocontext.IChain) (err error) { + if next != nil { + err = next.DoChain(ctx) + if err != nil { + return err + } + } + contentType := ctx.Response().ContentType() + for _, ct := range e.matchContentType { + if contentType == ct { + body := ctx.Response().GetBody() + res, err := injectJavaScript(string(body), e.injectCode) + if err != nil { + log.Error(err) + return nil + } + ctx.Response().SetBody([]byte(res)) + } + } + return +} + +func (e *executor) Destroy() { + return +} + +func (e *executor) Start() error { + return nil +} + +func (e *executor) Reset(conf interface{}, workers map[eosc.RequireId]eosc.IWorker) error { + return nil +} + +func (e *executor) reset(conf *Config) error { + decodeData, err := utils.B64Decode(conf.InjectCode) + if err != nil { + return err + } + injectCode := string(decodeData) + for _, v := range conf.Variables { + injectCode = strings.Replace(injectCode, fmt.Sprintf("{{%s}}", v.Key), v.Value, -1) + } + e.injectCode = fmt.Sprintf("%s", injectCode) + e.matchContentType = conf.MatchContentType + return nil +} + +func (e *executor) Stop() error { + e.Destroy() + return nil +} + +func (e *executor) CheckSkill(skill string) bool { + return http_service.FilterSkillName == skill +} diff --git a/drivers/plugins/js-inject/factory.go b/drivers/plugins/js-inject/factory.go new file mode 100644 index 00000000..2743eeed --- /dev/null +++ b/drivers/plugins/js-inject/factory.go @@ -0,0 +1,18 @@ +package js_inject + +import ( + "github.com/eolinker/apinto/drivers" + "github.com/eolinker/eosc" +) + +const ( + Name = "js_inject" +) + +func Register(register eosc.IExtenderDriverRegister) { + register.RegisterExtenderDriver(Name, NewFactory()) +} + +func NewFactory() eosc.IExtenderDriverFactory { + return drivers.NewFactory[Config](Create) +} diff --git a/drivers/plugins/js-inject/tool.go b/drivers/plugins/js-inject/tool.go new file mode 100644 index 00000000..05ca681a --- /dev/null +++ b/drivers/plugins/js-inject/tool.go @@ -0,0 +1,90 @@ +package js_inject + +import ( + "bytes" + "fmt" + + "golang.org/x/net/html" +) + +// injectJavaScript将JavaScript代码插入到HTML中 +func injectJavaScript(originalHTML, jsCode string) (string, error) { + // 解析 HTML 字符串 + doc, err := html.Parse(bytes.NewReader([]byte(originalHTML))) + if err != nil { + return "", fmt.Errorf("parse html error: %w", err) + } + node := findHead(doc) + if node == nil { + // 在每个 HTML 标签后添加
元素 + addHeadAfterTags(doc) + node = findHead(doc) + } + insertCodeInHead(node, jsCode) + // 将修改后的 HTML 打印出来 + var modifiedHTML bytes.Buffer + if err := html.Render(&modifiedHTML, doc); err != nil { + return "", fmt.Errorf("render html error: %w", err) + } + + return modifiedHTML.String(), nil + +} + +func findHead(node *html.Node) *html.Node { + if node.Type == html.ElementNode && node.Data == "head" { + return node + } + + for child := node.FirstChild; child != nil; child = child.NextSibling { + if found := findHead(child); found != nil { + return found + } + } + + return nil +} + +func addHeadAfterTags(node *html.Node) { + if node.Type == html.ElementNode { + // 创建 元素 + newHead := &html.Node{ + Type: html.ElementNode, + DataAtom: 0, + Data: "head", + } + // 在当前元素后插入 元素 + node.Parent.InsertBefore(newHead, node) + // 递归处理子节点 + for child := node.FirstChild; child != nil; child = child.NextSibling { + addHeadAfterTags(child) + } + } + + // 递归处理下一个兄弟节点 + if node.NextSibling != nil { + addHeadAfterTags(node.NextSibling) + } +} + +func insertCodeInHead(headNode *html.Node, jsCode string) { + // 创建一个新的