diff --git a/README.md b/README.md index 36e0ad78c3..c5ad2f3a56 100644 --- a/README.md +++ b/README.md @@ -34,13 +34,13 @@ Finished List: - Configure Center: Zookeeper - Cluster Strategy: Failover/[Failfast](https://github.com/apache/dubbo-go/pull/140)/[Failsafe/Failback](https://github.com/apache/dubbo-go/pull/136)/[Available](https://github.com/apache/dubbo-go/pull/155)/[Broadcast](https://github.com/apache/dubbo-go/pull/158)/[Forking](https://github.com/apache/dubbo-go/pull/161) - Load Balance: Random/[RoundRobin](https://github.com/apache/dubbo-go/pull/66)/[LeastActive](https://github.com/apache/dubbo-go/pull/65) -- Filter: Echo Health Check/[Circuit break and service downgrade](https://github.com/apache/dubbo-go/pull/133) +- Filter: Echo Health Check/[Circuit break and service downgrade](https://github.com/apache/dubbo-go/pull/133)/[TokenFilter](https://github.com/apache/dubbo-go/pull/202) - Other feature: [generic invoke](https://github.com/apache/dubbo-go/pull/122)/start check/connecting certain provider/multi-protocols/multi-registries/multi-versions/service group Working List: - Load Balance: ConsistentHash -- Filter: TokenFilter/AccessLogFilter/CountFilter/ExecuteLimitFilter/TpsLimitFilter +- Filter: AccessLogFilter/CountFilter/ExecuteLimitFilter/TpsLimitFilter - Registry: k8s - Configure Center: apollo - Dynamic Configuration Center & Metadata Center (dubbo v2.7.x) diff --git a/README_CN.md b/README_CN.md index 387070d267..266d1d0e92 100644 --- a/README_CN.md +++ b/README_CN.md @@ -33,14 +33,14 @@ Apache License, Version 2.0 - 配置中心: Zookeeper - 集群策略: Failover/[Failfast](https://github.com/apache/dubbo-go/pull/140)/[Failsafe/Failback](https://github.com/apache/dubbo-go/pull/136)/[Available](https://github.com/apache/dubbo-go/pull/155)/[Broadcast](https://github.com/apache/dubbo-go/pull/158)/[Forking](https://github.com/apache/dubbo-go/pull/161) - 负载均衡策略: Random/[RoundRobin](https://github.com/apache/dubbo-go/pull/66)/[LeastActive](https://github.com/apache/dubbo-go/pull/65) -- 过滤器: Echo Health Check/[服务熔断&降级](https://github.com/apache/dubbo-go/pull/133) +- 过滤器: Echo Health Check/[服务熔断&降级](https://github.com/apache/dubbo-go/pull/133)/[TokenFilter](https://github.com/apache/dubbo-go/pull/202) - 其他功能支持: [泛化调用](https://github.com/apache/dubbo-go/pull/122)/启动时检查/服务直连/多服务协议/多注册中心/多服务版本/服务分组 开发中列表: - 集群策略: Forking - 负载均衡策略: ConsistentHash -- 过滤器: TokenFilter/AccessLogFilter/CountFilter/ExecuteLimitFilter/TpsLimitFilter +- 过滤器: AccessLogFilter/CountFilter/ExecuteLimitFilter/TpsLimitFilter - 注册中心: k8s - 配置中心: apollo - 动态配置中心 & 元数据中心 (dubbo v2.7.x) diff --git a/common/constant/default.go b/common/constant/default.go index d937fcb3ed..405920e20f 100644 --- a/common/constant/default.go +++ b/common/constant/default.go @@ -46,7 +46,7 @@ const ( const ( DEFAULT_KEY = "default" PREFIX_DEFAULT_KEY = "default." - DEFAULT_SERVICE_FILTERS = "echo" + DEFAULT_SERVICE_FILTERS = "echo,token" DEFAULT_REFERENCE_FILTERS = "" GENERIC_REFERENCE_FILTERS = "generic" GENERIC = "$invoke" diff --git a/common/constant/key.go b/common/constant/key.go index 1b25d11edc..abb78c987b 100644 --- a/common/constant/key.go +++ b/common/constant/key.go @@ -37,6 +37,7 @@ const ( BEAN_NAME_KEY = "bean.name" GENERIC_KEY = "generic" CLASSIFIER_KEY = "classifier" + TOKEN_KEY = "token" ) const ( diff --git a/common/url.go b/common/url.go index 9f5b50264a..bf58ca188c 100644 --- a/common/url.go +++ b/common/url.go @@ -34,6 +34,7 @@ import ( "github.com/dubbogo/gost/container" "github.com/jinzhu/copier" perrors "github.com/pkg/errors" + "github.com/satori/go.uuid" ) import ( @@ -150,6 +151,18 @@ func WithLocation(location string) option { } } +func WithToken(token string) option { + return func(url *URL) { + if len(token) > 0 { + value := token + if strings.ToLower(token) == "true" || strings.ToLower(token) == "default" { + value = uuid.NewV4().String() + } + url.SetParam(constant.TOKEN_KEY, value) + } + } +} + func NewURLWithOptions(opts ...option) *URL { url := &URL{} for _, opt := range opts { diff --git a/config/service_config.go b/config/service_config.go index df6b8d1839..ce15c61eb3 100644 --- a/config/service_config.go +++ b/config/service_config.go @@ -57,6 +57,7 @@ type ServiceConfig struct { Warmup string `yaml:"warmup" json:"warmup,omitempty" property:"warmup"` Retries string `yaml:"retries" json:"retries,omitempty" property:"retries"` Params map[string]string `yaml:"params" json:"params,omitempty" property:"params"` + Token string `yaml:"token" json:"token,omitempty" property:"token"` unexported *atomic.Bool exported *atomic.Bool rpcService common.RPCService @@ -122,7 +123,10 @@ func (srvconfig *ServiceConfig) Export() error { common.WithPort(proto.Port), common.WithParams(urlMap), common.WithParamsValue(constant.BEAN_NAME_KEY, srvconfig.id), - common.WithMethods(strings.Split(methods, ","))) + common.WithMethods(strings.Split(methods, ",")), + common.WithToken(srvconfig.Token), + ) + if len(regUrls) > 0 { for _, regUrl := range regUrls { regUrl.SubURL = url diff --git a/examples/helloworld/dubbo/go-server/profiles/dev/server.yml b/examples/helloworld/dubbo/go-server/profiles/dev/server.yml index 27e9d55c8d..8e8594a1ce 100644 --- a/examples/helloworld/dubbo/go-server/profiles/dev/server.yml +++ b/examples/helloworld/dubbo/go-server/profiles/dev/server.yml @@ -30,6 +30,7 @@ services: - name: "GetUser" retries: "1" loadbalance: "random" + token: "true" protocols: "dubbo": diff --git a/filter/impl/token_filter.go b/filter/impl/token_filter.go new file mode 100644 index 0000000000..d10dff5b76 --- /dev/null +++ b/filter/impl/token_filter.go @@ -0,0 +1,66 @@ +/* +Licensed to the Apache Software Foundation (ASF) under one or more +contributor license agreements. See the NOTICE file distributed with +this work for additional information regarding copyright ownership. +The ASF licenses this file to You under the Apache License, Version 2.0 +(the "License"); you may not use this file except in compliance with +the License. You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package impl + +import ( + "strings" +) + +import ( + perrors "github.com/pkg/errors" +) + +import ( + "github.com/apache/dubbo-go/common/constant" + "github.com/apache/dubbo-go/common/extension" + "github.com/apache/dubbo-go/filter" + "github.com/apache/dubbo-go/protocol" +) + +const ( + TOKEN = "token" +) + +func init() { + extension.SetFilter(TOKEN, GetTokenFilter) +} + +type TokenFilter struct{} + +func (tf *TokenFilter) Invoke(invoker protocol.Invoker, invocation protocol.Invocation) protocol.Result { + invokerTkn := invoker.GetUrl().GetParam(constant.TOKEN_KEY, "") + if len(invokerTkn) > 0 { + attachs := invocation.Attachments() + remoteTkn, exist := attachs[constant.TOKEN_KEY] + if exist && strings.EqualFold(invokerTkn, remoteTkn) { + return invoker.Invoke(invocation) + } + return &protocol.RPCResult{Err: perrors.Errorf("Invalid token! Forbid invoke remote service %v method %s ", + invoker, invocation.MethodName())} + } + + return invoker.Invoke(invocation) +} + +func (tf *TokenFilter) OnResponse(result protocol.Result, invoker protocol.Invoker, invocation protocol.Invocation) protocol.Result { + return result +} + +func GetTokenFilter() filter.Filter { + return &TokenFilter{} +} diff --git a/filter/impl/token_filter_test.go b/filter/impl/token_filter_test.go new file mode 100644 index 0000000000..1473f27403 --- /dev/null +++ b/filter/impl/token_filter_test.go @@ -0,0 +1,85 @@ +/* +Licensed to the Apache Software Foundation (ASF) under one or more +contributor license agreements. See the NOTICE file distributed with +this work for additional information regarding copyright ownership. +The ASF licenses this file to You under the Apache License, Version 2.0 +(the "License"); you may not use this file except in compliance with +the License. You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package impl + +import ( + "net/url" + "testing" +) + +import ( + "github.com/stretchr/testify/assert" +) + +import ( + "github.com/apache/dubbo-go/common" + "github.com/apache/dubbo-go/common/constant" + "github.com/apache/dubbo-go/protocol" + "github.com/apache/dubbo-go/protocol/invocation" +) + +func TestTokenFilter_Invoke(t *testing.T) { + filter := GetTokenFilter() + + url := common.NewURLWithOptions( + common.WithParams(url.Values{}), + common.WithParamsValue(constant.TOKEN_KEY, "ori_key")) + attch := make(map[string]string, 0) + attch[constant.TOKEN_KEY] = "ori_key" + result := filter.Invoke(protocol.NewBaseInvoker(*url), + invocation.NewRPCInvocation("MethodName", []interface{}{"OK"}, attch)) + assert.Nil(t, result.Error()) + assert.Nil(t, result.Result()) +} + +func TestTokenFilter_InvokeEmptyToken(t *testing.T) { + filter := GetTokenFilter() + + url := common.URL{} + attch := make(map[string]string, 0) + attch[constant.TOKEN_KEY] = "ori_key" + result := filter.Invoke(protocol.NewBaseInvoker(url), + invocation.NewRPCInvocation("MethodName", []interface{}{"OK"}, attch)) + assert.Nil(t, result.Error()) + assert.Nil(t, result.Result()) +} + +func TestTokenFilter_InvokeEmptyAttach(t *testing.T) { + filter := GetTokenFilter() + + url := common.NewURLWithOptions( + common.WithParams(url.Values{}), + common.WithParamsValue(constant.TOKEN_KEY, "ori_key")) + attch := make(map[string]string, 0) + result := filter.Invoke(protocol.NewBaseInvoker(*url), + invocation.NewRPCInvocation("MethodName", []interface{}{"OK"}, attch)) + assert.NotNil(t, result.Error()) +} + +func TestTokenFilter_InvokeNotEqual(t *testing.T) { + filter := GetTokenFilter() + + url := common.NewURLWithOptions( + common.WithParams(url.Values{}), + common.WithParamsValue(constant.TOKEN_KEY, "ori_key")) + attch := make(map[string]string, 0) + attch[constant.TOKEN_KEY] = "err_key" + result := filter.Invoke(protocol.NewBaseInvoker(*url), + invocation.NewRPCInvocation("MethodName", []interface{}{"OK"}, attch)) + assert.NotNil(t, result.Error()) +} diff --git a/go.mod b/go.mod index ec3c24c278..09948c93c5 100644 --- a/go.mod +++ b/go.mod @@ -35,6 +35,7 @@ require ( github.com/pkg/errors v0.8.1 github.com/prometheus/client_golang v1.1.0 // indirect github.com/samuel/go-zookeeper v0.0.0-20180130194729-c4fab1ac1bec + github.com/satori/go.uuid v1.2.0 github.com/smartystreets/goconvey v0.0.0-20190710185942-9d28bd7c0945 // indirect github.com/soheilhy/cmux v0.1.4 // indirect github.com/stretchr/testify v1.3.0 diff --git a/protocol/dubbo/dubbo_invoker.go b/protocol/dubbo/dubbo_invoker.go index 4bfc1324cf..bc321a97a4 100644 --- a/protocol/dubbo/dubbo_invoker.go +++ b/protocol/dubbo/dubbo_invoker.go @@ -36,6 +36,10 @@ import ( var Err_No_Reply = perrors.New("request need @response") +var ( + attachmentKey = []string{constant.INTERFACE_KEY, constant.GROUP_KEY, constant.TOKEN_KEY, constant.TIMEOUT_KEY} +) + type DubboInvoker struct { protocol.BaseInvoker client *Client @@ -57,6 +61,11 @@ func (di *DubboInvoker) Invoke(invocation protocol.Invocation) protocol.Result { ) inv := invocation.(*invocation_impl.RPCInvocation) + for _, k := range attachmentKey { + if v := di.GetUrl().GetParam(k, ""); len(v) > 0 { + inv.SetAttachments(k, v) + } + } url := di.GetUrl() // async async, err := strconv.ParseBool(inv.AttachmentsByKey(constant.ASYNC_KEY, "false"))