Skip to content

Commit

Permalink
新增js拦截器、rsa加密插件
Browse files Browse the repository at this point in the history
  • Loading branch information
Dot-Liu committed Dec 24, 2023
1 parent f79234d commit c27b32d
Show file tree
Hide file tree
Showing 13 changed files with 528 additions and 6 deletions.
4 changes: 4 additions & 0 deletions app/apinto/plugin.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,13 +8,15 @@ 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"
request_interception "github.com/eolinker/apinto/drivers/plugins/request-interception"
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"
Expand Down Expand Up @@ -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)
Expand Down
1 change: 0 additions & 1 deletion drivers/discovery/nacos/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down
25 changes: 25 additions & 0 deletions drivers/plugins/js-inject/config.go
Original file line number Diff line number Diff line change
@@ -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
}
86 changes: 86 additions & 0 deletions drivers/plugins/js-inject/executor.go
Original file line number Diff line number Diff line change
@@ -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
}
18 changes: 18 additions & 0 deletions drivers/plugins/js-inject/factory.go
Original file line number Diff line number Diff line change
@@ -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)
}
90 changes: 90 additions & 0 deletions drivers/plugins/js-inject/tool.go
Original file line number Diff line number Diff line change
@@ -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 标签后添加 <head> 元素
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 {
// 创建 <head> 元素
newHead := &html.Node{
Type: html.ElementNode,
DataAtom: 0,
Data: "head",
}
// 在当前元素后插入 <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) {
// 创建一个新的 <script> 元素(示例代码)
newScript := &html.Node{
Type: html.ElementNode,
Data: "script",
DataAtom: 0,
}

// 在 <script> 中插入要添加的代码
newScript.AppendChild(&html.Node{
Type: html.TextNode,
Data: jsCode,
})

// 将新的 <script> 元素插入到 <head> 的第一个子标签位置
if headNode.LastChild != nil {
headNode.InsertBefore(newScript, headNode.FirstChild)
} else {
headNode.AppendChild(newScript)
}
}
22 changes: 22 additions & 0 deletions drivers/plugins/rsa-filter/config.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
package rsa_filter

import (
"github.com/eolinker/apinto/drivers"
"github.com/eolinker/eosc"
)

type Config struct {
PrivateKey string `json:"private_key" label:"私钥" description:"对请求体进行解密,对响应体进行签名"`
PublicKey string `json:"public_key" label:"公钥" description:"对请求体进行验签,对响应体进行加密"`
RequestSignHeader string `json:"request_sign_header" label:"请求签名头"`
ResponseSignHeader string `json:"response_sign_header" label:"响应签名头"`
Format string `json:"format" label:"密钥格式" enum:"origin,base64"`
}

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
}
Loading

0 comments on commit c27b32d

Please sign in to comment.