diff --git a/.github/workflows/github-actions.yml b/.github/workflows/github-actions.yml index 6252b5def..8ec3ce53a 100644 --- a/.github/workflows/github-actions.yml +++ b/.github/workflows/github-actions.yml @@ -87,10 +87,10 @@ jobs: # diff -u <(echo -n) <(gofmt -d -s .) - name: Install go ci lint - run: curl -sSfL https://raw.githubusercontent.com/golangci/golangci-lint/master/install.sh | sh -s -- -b $(go env GOPATH)/bin v1.27.0 + run: curl -sSfL https://raw.githubusercontent.com/golangci/golangci-lint/master/install.sh | sh -s -- -b $(go env GOPATH)/bin v1.41.1 - name: Run Linter - run: golangci-lint run --timeout=10m -v --disable-all --enable=govet --enable=staticcheck --enable=ineffassign --enable=misspell + run: GO111MODULE=on golangci-lint run --timeout=10m -v --disable-all --enable=govet --enable=staticcheck --enable=ineffassign --enable=misspell - name: Go Test run: | diff --git a/docs/sample/others/auth-filter.md b/docs/sample/others/auth-filter.md new file mode 100644 index 000000000..4484e9a8f --- /dev/null +++ b/docs/sample/others/auth-filter.md @@ -0,0 +1,95 @@ +# Auth Filter Quick Start + +## Http + +Start Zookeeper [Docker environment]: + +```bash +cd samples/dubbogo/http/docker +run docker-compose.yml/services +``` + +Start Http [Go environment]: + +```bash +go run samples/dubbogo/simple/jwt/server/server.go +``` + +Start Pixiu: + +```bash +go run cmd/pixiu/*.go gateway start -c samples/dubbogo/simple/jwt/pixiu/conf.yaml +``` + +Call the server of Http by Pixiu : + +- default Authorization: Bearer + +```bash +curl -H "Authorization: Bearer eyJraWQiOiJlZThkNjI2ZCIsInR5cCI6IkpXVCIsImFsZyI6IlJTMjU2In0.eyJzdWIiOiJXZWlkb25nIiwiYXVkIjoiVGFzaHVhbiIsImlzcyI6Imp3a3Mtc2VydmljZS5hcHBzcG90LmNvbSIsImlhdCI6MTYzMTM2OTk1NSwianRpIjoiNDY2M2E5MTAtZWU2MC00NzcwLTgxNjktY2I3NDdiMDljZjU0In0.LwD65d5h6U_2Xco81EClMa_1WIW4xXZl8o4b7WzY_7OgPD2tNlByxvGDzP7bKYA9Gj--1mi4Q4li4CAnKJkaHRYB17baC0H5P9lKMPuA6AnChTzLafY6yf-YadA7DmakCtIl7FNcFQQL2DXmh6gS9J6TluFoCIXj83MqETbDWpL28o3XAD_05UP8VLQzH2XzyqWKi97mOuvz-GsDp9mhBYQUgN3csNXt2v2l-bUPWe19SftNej0cxddyGu06tXUtaS6K0oe0TTbaqc3hmfEiu5G0J8U6ztTUMwXkBvaknE640NPgMQJqBaey0E4u0txYgyvMvvxfwtcOrDRYqYPBnA" http://localhost:8888/user/pixiu +``` + +result on console : + +```log +{"message":"user","status":200} +``` + +Token invalid + +```bash +curl http://localhost:8888/health +``` + +result on console : + +```log +{"message":"token invalid","status":401} +``` + + + +## Spring Cloud + +Start Nacos [Docker environment]: + +```bash +cd samples/springcloud/docker +run docker-compose.yml/services +``` + +Start SpringCloud [Java environment]: + +```bash +cd samples/springcloud/server + +# the port is 8074 +run auth-service + +# the port is 8071 +run user-service +``` + +Start Pixiu: + +```bash +go run cmd/pixiu/*.go gateway start -c samples/dubbogo/simple/jwt/pixiu/springcloud-conf.yaml +``` + +Call the server of SpringCloud by Pixiu : + +- default Authorization: Bearer + +```bash +# the serviceId is `user-provider` +curl -H "Authorization: Bearer eyJraWQiOiJlZThkNjI2ZCIsInR5cCI6IkpXVCIsImFsZyI6IlJTMjU2In0.eyJzdWIiOiJXZWlkb25nIiwiYXVkIjoiVGFzaHVhbiIsImlzcyI6Imp3a3Mtc2VydmljZS5hcHBzcG90LmNvbSIsImlhdCI6MTYzMTM2OTk1NSwianRpIjoiNDY2M2E5MTAtZWU2MC00NzcwLTgxNjktY2I3NDdiMDljZjU0In0.LwD65d5h6U_2Xco81EClMa_1WIW4xXZl8o4b7WzY_7OgPD2tNlByxvGDzP7bKYA9Gj--1mi4Q4li4CAnKJkaHRYB17baC0H5P9lKMPuA6AnChTzLafY6yf-YadA7DmakCtIl7FNcFQQL2DXmh6gS9J6TluFoCIXj83MqETbDWpL28o3XAD_05UP8VLQzH2XzyqWKi97mOuvz-GsDp9mhBYQUgN3csNXt2v2l-bUPWe19SftNej0cxddyGu06tXUtaS6K0oe0TTbaqc3hmfEiu5G0J8U6ztTUMwXkBvaknE640NPgMQJqBaey0E4u0txYgyvMvvxfwtcOrDRYqYPBnA" http://localhost:8888/user-service/echo/Pixiu + +# the serviceId is `auth-provider` +curl -H "Authorization: Bearer eyJraWQiOiJlZThkNjI2ZCIsInR5cCI6IkpXVCIsImFsZyI6IlJTMjU2In0.eyJzdWIiOiJXZWlkb25nIiwiYXVkIjoiVGFzaHVhbiIsImlzcyI6Imp3a3Mtc2VydmljZS5hcHBzcG90LmNvbSIsImlhdCI6MTYzMTM2OTk1NSwianRpIjoiNDY2M2E5MTAtZWU2MC00NzcwLTgxNjktY2I3NDdiMDljZjU0In0.LwD65d5h6U_2Xco81EClMa_1WIW4xXZl8o4b7WzY_7OgPD2tNlByxvGDzP7bKYA9Gj--1mi4Q4li4CAnKJkaHRYB17baC0H5P9lKMPuA6AnChTzLafY6yf-YadA7DmakCtIl7FNcFQQL2DXmh6gS9J6TluFoCIXj83MqETbDWpL28o3XAD_05UP8VLQzH2XzyqWKi97mOuvz-GsDp9mhBYQUgN3csNXt2v2l-bUPWe19SftNej0cxddyGu06tXUtaS6K0oe0TTbaqc3hmfEiu5G0J8U6ztTUMwXkBvaknE640NPgMQJqBaey0E4u0txYgyvMvvxfwtcOrDRYqYPBnA" http://localhost:8888/auth-service/echo/Pixiu +``` + +result on console : + +```log +Hello {service_name} Pixiu +``` \ No newline at end of file diff --git a/go.mod b/go.mod index 87146f9a2..7b925f699 100644 --- a/go.mod +++ b/go.mod @@ -4,6 +4,7 @@ go 1.15 require ( dubbo.apache.org/dubbo-go/v3 v3.0.0-rc4-1 + github.com/MicahParks/keyfunc v0.10.0 github.com/Shopify/sarama v1.19.0 github.com/alibaba/sentinel-golang v1.0.2 github.com/apache/dubbo-go v1.5.7 @@ -18,6 +19,7 @@ require ( github.com/go-resty/resty/v2 v2.7.0 github.com/gogo/protobuf v1.3.2 github.com/goinggo/mapstructure v0.0.0-20140717182941-194205d9b4a9 + github.com/golang-jwt/jwt/v4 v4.1.0 github.com/golang/protobuf v1.5.2 github.com/jhump/protoreflect v1.9.0 github.com/mercari/grpc-http-proxy v0.1.2 @@ -51,4 +53,4 @@ require ( replace github.com/envoyproxy/go-control-plane => github.com/envoyproxy/go-control-plane v0.8.0 -replace github.com/go-co-op/gocron => github.com/go-co-op/gocron v0.1.1 +replace github.com/go-co-op/gocron => github.com/go-co-op/gocron v0.1.1 \ No newline at end of file diff --git a/go.sum b/go.sum index ead207aa6..6f345569f 100644 --- a/go.sum +++ b/go.sum @@ -64,6 +64,8 @@ github.com/DataDog/datadog-go v2.2.0+incompatible/go.mod h1:LButxg5PwREeZtORoXG3 github.com/DataDog/datadog-go v3.2.0+incompatible/go.mod h1:LButxg5PwREeZtORoXG3tL4fMGNddJ+vMq1mwgfaqoQ= github.com/Julusian/godocdown v0.0.0-20170816220326-6d19f8ff2df8/go.mod h1:INZr5t32rG59/5xeltqoCJoNY7e5x/3xoY9WSWVWg74= github.com/Knetic/govaluate v3.0.1-0.20171022003610-9aa49832a739+incompatible/go.mod h1:r7JcOSlj0wfOMncg0iLm8Leh48TZaKVeNIfJntJ2wa0= +github.com/MicahParks/keyfunc v0.10.0 h1:jWNhUVtMchsdYVl714lrZL8On+SKPEvPKC+CpIN7HwE= +github.com/MicahParks/keyfunc v0.10.0/go.mod h1:R8RZa27qn+5cHTfYLJ9/+7aSb5JIdz7cl0XFo0o4muo= github.com/Microsoft/go-winio v0.4.3/go.mod h1:VhR8bwka0BXejwEJY73c50VrPtXAaKcyvVC4A4RozmA= github.com/NYTimes/gziphandler v0.0.0-20170623195520-56545f4a5d46/go.mod h1:3wb06e3pkSAbeQ52E9H9iFoQsEEwGN64994WTCIhntQ= github.com/NYTimes/gziphandler v1.0.1/go.mod h1:3wb06e3pkSAbeQ52E9H9iFoQsEEwGN64994WTCIhntQ= @@ -223,6 +225,8 @@ github.com/dubbogo/gost v1.11.12/go.mod h1:vIcP9rqz2KsXHPjsAwIUtfJIJjppQLQDcYaZT github.com/dubbogo/gost v1.11.14/go.mod h1:vIcP9rqz2KsXHPjsAwIUtfJIJjppQLQDcYaZTy/61jI= github.com/dubbogo/gost v1.11.18/go.mod h1:vIcP9rqz2KsXHPjsAwIUtfJIJjppQLQDcYaZTy/61jI= github.com/dubbogo/gost v1.11.19 h1:R1rZ3TNJKV9W5XHLMv+GDO2Wy6UDnwGQtVWbsWYvo0A= +github.com/dubbogo/gost v1.11.19 h1:R1rZ3TNJKV9W5XHLMv+GDO2Wy6UDnwGQtVWbsWYvo0A= +github.com/dubbogo/gost v1.11.19/go.mod h1:vIcP9rqz2KsXHPjsAwIUtfJIJjppQLQDcYaZTy/61jI= github.com/dubbogo/gost v1.11.19/go.mod h1:vIcP9rqz2KsXHPjsAwIUtfJIJjppQLQDcYaZTy/61jI= github.com/dubbogo/grpc-go v1.42.5-triple/go.mod h1:F1T9hnUvYGW4JLK1QNriavpOkhusU677ovPzLkk6zHM= github.com/dubbogo/jsonparser v1.0.1/go.mod h1:tYAtpctvSP/tWw4MeelsowSPgXQRVHHWbqL6ynps8jU= @@ -264,6 +268,7 @@ github.com/form3tech-oss/jwt-go v3.2.2+incompatible h1:TcekIExNqud5crz4xD2pavyTg github.com/form3tech-oss/jwt-go v3.2.2+incompatible/go.mod h1:pbq4aXjuKjdthFRnoDwaVPLA+WlJuPGy+QneDUgJi2k= github.com/franela/goblin v0.0.0-20200105215937-c9ffbefa60db/go.mod h1:7dvUGVsVBjqR7JHJk0brhHOZYGmfBYOrK0ZhYMEtBr4= github.com/franela/goreq v0.0.0-20171204163338-bcd34c9993f8/go.mod h1:ZhphrRTfi2rbfLwlschooIH4+wKKDR4Pdxhh+TRoA20= +github.com/frankban/quicktest v1.10.0 h1:Gfh+GAJZOAoKZsIZeZbdn2JF10kN1XHNvjsvQK8gVkE= github.com/frankban/quicktest v1.10.0/go.mod h1:ui7WezCLWMWxVWr1GETZY3smRy0G4KWq9vcPtJmFl7Y= github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ= @@ -347,6 +352,8 @@ github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69 github.com/goinggo/mapstructure v0.0.0-20140717182941-194205d9b4a9 h1:wqckanyE9qc/XnvnybC6SHOb8Nyd62QXAZOzA8twFig= github.com/goinggo/mapstructure v0.0.0-20140717182941-194205d9b4a9/go.mod h1:64ikIrMv84B+raz7akXOqbF7cK3/OQQ/6cClY10oy7A= github.com/goji/httpauth v0.0.0-20160601135302-2da839ab0f4d/go.mod h1:nnjvkQ9ptGaCkuDUx6wNykzzlUixGxvkme+H/lnzb+A= +github.com/golang-jwt/jwt/v4 v4.1.0 h1:XUgk2Ex5veyVFVeLm0xhusUTQybEbexJXrvPNOKkSY0= +github.com/golang-jwt/jwt/v4 v4.1.0/go.mod h1:/xlHOz8bRuivTWchD4jCa+NbatV+wEUSzwAxVc6locg= github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= github.com/golang/glog v1.0.0/go.mod h1:EWib/APOK0SL3dFbYqvxE3UYd8E6s1ouQ7iEp/0LWV4= github.com/golang/groupcache v0.0.0-20160516000752-02826c3e7903/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= @@ -741,7 +748,6 @@ github.com/performancecopilot/speed v3.0.0+incompatible/go.mod h1:/CLtqpZ5gBg1M9 github.com/peterbourgon/diskv v2.0.1+incompatible/go.mod h1:uqqh8zWWbv1HBMNONnaR/tNboyR3/BZd58JJSHlUSCU= github.com/philhofer/fwd v1.0.0/go.mod h1:gk3iGcWd9+svBvR0sR+KPcfE+RNWozjowpeBVG3ZVNU= github.com/pierrec/lz4 v1.0.2-0.20190131084431-473cd7ce01a1/go.mod h1:3/3N9NVKO0jef7pBehbT1qWhCMrIgbYNnFAZCqQ5LRc= -github.com/pierrec/lz4 v2.0.5+incompatible h1:2xWsjqPFWcplujydGg4WmhC/6fZqK42wMM8aXeqhl0I= github.com/pierrec/lz4 v2.0.5+incompatible/go.mod h1:pdkljMzZIN41W+lC3N2tnIh5sFi+IEE17M5jbnwPHcY= github.com/pierrec/lz4 v2.5.2+incompatible h1:WCjObylUIOlKy/+7Abdn34TLIkXiA4UWUMhxq9m9ZXI= github.com/pierrec/lz4 v2.5.2+incompatible/go.mod h1:pdkljMzZIN41W+lC3N2tnIh5sFi+IEE17M5jbnwPHcY= @@ -1347,8 +1353,11 @@ google.golang.org/genproto v0.0.0-20200806141610-86f49bd18e98/go.mod h1:FWY/as6D google.golang.org/genproto v0.0.0-20200825200019-8632dd797987/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= google.golang.org/genproto v0.0.0-20210106152847-07624b53cd92/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= google.golang.org/genproto v0.0.0-20210602131652-f16073e35f0c/go.mod h1:UODoCrxHCcBojKKwX1terBiRUaqAsFqJiF615XL43r0= +google.golang.org/genproto v0.0.0-20210602131652-f16073e35f0c/go.mod h1:UODoCrxHCcBojKKwX1terBiRUaqAsFqJiF615XL43r0= +google.golang.org/genproto v0.0.0-20211104193956-4c6863e31247 h1:ZONpjmFT5e+I/0/xE3XXbG5OIvX2hRYzol04MhKBl2E= google.golang.org/genproto v0.0.0-20211104193956-4c6863e31247 h1:ZONpjmFT5e+I/0/xE3XXbG5OIvX2hRYzol04MhKBl2E= google.golang.org/genproto v0.0.0-20211104193956-4c6863e31247/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc= +google.golang.org/genproto v0.0.0-20211104193956-4c6863e31247/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc= google.golang.org/grpc v1.8.0/go.mod h1:yo6s7OP7yaDglbqo1J04qKzAhqBH6lvTonzMVmEdcZw= google.golang.org/grpc v1.14.0/go.mod h1:yo6s7OP7yaDglbqo1J04qKzAhqBH6lvTonzMVmEdcZw= google.golang.org/grpc v1.17.0/go.mod h1:6QZJwpn2B+Zp71q/5VxRsJ6NXXVCE5NRUHRo+f3cWCs= @@ -1374,7 +1383,10 @@ google.golang.org/grpc v1.32.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM google.golang.org/grpc v1.33.1/go.mod h1:fr5YgcSWrqhRRxogOsw7RzIpsmvOZ6IcH4kBYTpR3n0= google.golang.org/grpc v1.33.2/go.mod h1:JMHMWHQWaTccqQQlmk3MJZS+GWXOdAesneDmEnv2fbc= google.golang.org/grpc v1.36.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU= +google.golang.org/grpc v1.36.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU= google.golang.org/grpc v1.38.0/go.mod h1:NREThFqKR1f3iQ6oBuvc5LadQuXVGo9rkm5ZGrQdJfM= +google.golang.org/grpc v1.38.0/go.mod h1:NREThFqKR1f3iQ6oBuvc5LadQuXVGo9rkm5ZGrQdJfM= +google.golang.org/grpc v1.40.0/go.mod h1:ogyxbiOoUXAkP+4+xa6PZSE9DZgIHtSpzjDTB9KAK34= google.golang.org/grpc v1.40.0/go.mod h1:ogyxbiOoUXAkP+4+xa6PZSE9DZgIHtSpzjDTB9KAK34= google.golang.org/grpc v1.41.0/go.mod h1:U3l9uK9J0sini8mHphKoXyaqDA/8VyGnDee1zzIUK6k= google.golang.org/grpc v1.42.0 h1:XT2/MFpuPFsEX2fWh3YQtHkZ+WYZFQRfaUgLZYj/p6A= diff --git a/pkg/common/constant/key.go b/pkg/common/constant/key.go index e90a73ee3..884ee898e 100644 --- a/pkg/common/constant/key.go +++ b/pkg/common/constant/key.go @@ -34,6 +34,7 @@ const ( HTTPApiConfigFilter = "dgp.filter.http.apiconfig" HTTPTimeoutFilter = "dgp.filter.http.timeout" TracingFilter = "dgp.filters.tracing" + HTTPAuthJwtFilter = "dgp.filter.http.auth.jwt" HTTPCorsFilter = "dgp.filter.http.cors" HTTPCsrfFilter = "dgp.filter.http.csrf" HTTPProxyRewriteFilter = "dgp.filter.http.proxyrewrite" diff --git a/pkg/filter/auth/jwt/config.go b/pkg/filter/auth/jwt/config.go new file mode 100644 index 000000000..f1a0b6926 --- /dev/null +++ b/pkg/filter/auth/jwt/config.go @@ -0,0 +1,76 @@ +/* + * 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 jwt + +import "github.com/MicahParks/keyfunc" + +type ( + // FromHeaders Get the token from a field in the header,default Authorization: Bearer + FromHeaders struct { + Name string `default:"Authorization" yaml:"name" json:"name" mapstructure:"name"` + ValuePrefix string `default:"Bearer " yaml:"value_prefix" json:"value_prefix" mapstructure:"value_prefix"` + } + + Rules struct { + Match Match `yaml:"match" json:"match" mapstructure:"match"` + Requires Requires `yaml:"requires" json:"requires" mapstructure:"requires"` + } + + Match struct { + Prefix string `yaml:"prefix" json:"prefix" mapstructure:"prefix"` + } + + Requires struct { + RequiresAny Requirement `yaml:"requires_any" json:"requires_any" mapstructure:"requires_any"` + RequiresAll []Requirement `yaml:"requires_all" json:"requires_all" mapstructure:"requires_all"` + } + + Requirement struct { + ProviderName string `yaml:"provider_name" json:"provider_name" mapstructure:"provider_name"` + } + + Providers struct { + Name string `yaml:"name" json:"name" mapstructure:"name"` + ForwardPayloadHeader string `yaml:"forward_payload_header" json:"forward_payload_header" mapstructure:"forward_payload_header"` + FromHeaders FromHeaders `yaml:"from_headers" json:"from_headers" mapstructure:"from_headers"` + Issuer string `yaml:"issuer" json:"issuer" mapstructure:"issuer"` + Local *Local `yaml:"local_jwks" json:"local_jwks" mapstructure:"local_jwks"` + Remote *Remote `yaml:"remote_jwks" json:"remote_jwks" mapstructure:"remote_jwks"` + } + + Local struct { + InlineString string `yaml:"inline_string" json:"inline_string" mapstructure:"inline_string"` + } + + Remote struct { + HttpURI HttpURI `yaml:"http_uri" json:"http_uri" mapstructure:"http_uri"` + } + + HttpURI struct { + Uri string `yaml:"uri" json:"uri" mapstructure:"uri"` + Cluster string `yaml:"cluster" json:"cluster" mapstructure:"cluster"` + TimeOut string `default:"5s" yaml:"timeout" json:"timeout" mapstructure:"timeout"` + } +) + +type Provider struct { + jwk *keyfunc.JWKs + issuer string + forwardPayloadHeader string + headers FromHeaders +} diff --git a/pkg/filter/auth/jwt/jwt.go b/pkg/filter/auth/jwt/jwt.go new file mode 100644 index 000000000..290ad1998 --- /dev/null +++ b/pkg/filter/auth/jwt/jwt.go @@ -0,0 +1,224 @@ +/* + * 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 jwt + +import ( + "encoding/json" + "fmt" + stdHttp "net/http" + "strings" + "time" +) + +import ( + "github.com/MicahParks/keyfunc" + jwt4 "github.com/golang-jwt/jwt/v4" +) + +import ( + "github.com/apache/dubbo-go-pixiu/pkg/common/constant" + "github.com/apache/dubbo-go-pixiu/pkg/common/extension/filter" + "github.com/apache/dubbo-go-pixiu/pkg/context/http" + "github.com/apache/dubbo-go-pixiu/pkg/logger" +) + +const ( + Kind = constant.HTTPAuthJwtFilter +) + +func init() { + filter.RegisterHttpFilter(&Plugin{}) +} + +type ( + // Plugin is http filter plugin. + Plugin struct { + } + + // Filter is http filter instance + Filter struct { + cfg *Config + errMsg []byte + providerJwks map[string]Provider + } + + // Config describe the config of Filter + Config struct { + ErrMsg string `yaml:"err_msg" json:"err_msg" mapstructure:"err_msg"` + Rules []Rules `yaml:"rules" json:"rules" mapstructure:"rules"` + Providers []Providers `yaml:"providers" json:"providers" mapstructure:"providers"` + } +) + +func (p Plugin) Kind() string { + return Kind +} + +func (p *Plugin) CreateFilter() (filter.HttpFilter, error) { + return &Filter{cfg: &Config{}, providerJwks: map[string]Provider{}}, nil +} + +func (f *Filter) PrepareFilterChain(ctx *http.HttpContext) error { + ctx.AppendFilterFunc(f.Handle) + return nil +} + +func (f *Filter) Handle(ctx *http.HttpContext) { + + path := ctx.Request.RequestURI + + router := false + + for _, rule := range f.cfg.Rules { + if strings.HasPrefix(path, rule.Match.Prefix) { + router = true + if f.validAny(rule, ctx) || f.validAll(rule, ctx) { + router = false + break + } + } + } + + if router { + ctx.WriteJSONWithStatus(stdHttp.StatusUnauthorized, f.errMsg) + ctx.Abort() + return + } + + ctx.Next() + +} + +func valuePrefix(value, prefix string) string { + if prefix == "" { + return value + } + return strings.TrimPrefix(value, prefix) +} + +// validAny route single provider verification +func (f *Filter) validAny(rule Rules, ctx *http.HttpContext) bool { + + providerName := rule.Requires.RequiresAny.ProviderName + + if provider, ok := f.providerJwks[providerName]; ok { + ctx.Request.Header.Set(provider.forwardPayloadHeader, provider.issuer) + if key := ctx.Request.Header.Get(provider.headers.Name); key != "" { + token, err := jwt4.Parse(valuePrefix(key, provider.headers.ValuePrefix), provider.jwk.Keyfunc) + if err != nil { + logger.Warnf("failed to parse JWKs from JSON. provider:%s Error: %s", providerName, err.Error()) + return false + } + return token.Valid + } + } + + return false +} + +// validAll route multiple provider verification +func (f *Filter) validAll(rule Rules, ctx *http.HttpContext) bool { + + for _, requirement := range rule.Requires.RequiresAll { + if provider, ok := f.providerJwks[requirement.ProviderName]; ok { + ctx.Request.Header.Set(provider.forwardPayloadHeader, provider.issuer) + if key := ctx.Request.Header.Get(provider.headers.Name); key != "" { + token, err := jwt4.Parse(valuePrefix(key, provider.headers.ValuePrefix), provider.jwk.Keyfunc) + if err != nil { + logger.Warnf("failed to parse JWKs from JSON. provider:%s Error: %s", requirement.ProviderName, err.Error()) + continue + } + + if token.Valid { + return true + } + } + } + } + + return false +} + +func (f *Filter) Apply() error { + + if f.cfg.ErrMsg == "" { + f.cfg.ErrMsg = "token invalid" + } + + errMsg, _ := json.Marshal(http.ErrResponse{Message: f.cfg.ErrMsg}) + f.errMsg = errMsg + + if len(f.cfg.Providers) == 0 { + return fmt.Errorf("providers is null") + } + + for _, provider := range f.cfg.Providers { + + if provider.Local != nil { + jwksJSON := json.RawMessage(provider.Local.InlineString) + jwks, err := keyfunc.NewJSON(jwksJSON) + if err != nil { + logger.Warnf("failed to create JWKs from JSON. provider:%s Error: %s", provider.Name, err.Error()) + } else { + provider.FromHeaders.setDefault() + f.providerJwks[provider.Name] = Provider{jwk: jwks, headers: provider.FromHeaders, + issuer: provider.Issuer, forwardPayloadHeader: provider.ForwardPayloadHeader} + continue + } + } + + if provider.Remote != nil { + uri := provider.Remote.HttpURI + timeout, err := time.ParseDuration(uri.TimeOut) + if err != nil { + logger.Warnf("jwt provides timeout parse fail: %s", err.Error()) + continue + } + + options := keyfunc.Options{RefreshTimeout: timeout} + jwks, err := keyfunc.Get(uri.Uri, options) + if err != nil { + logger.Warnf("failed to create JWKs from resource at the given URL. provider:%s Error: %s", provider.Name, err.Error()) + } else { + provider.FromHeaders.setDefault() + f.providerJwks[provider.Name] = Provider{jwk: jwks, headers: provider.FromHeaders, + issuer: provider.Issuer, forwardPayloadHeader: provider.ForwardPayloadHeader} + } + } + } + + if len(f.providerJwks) == 0 { + return fmt.Errorf("providers is null") + } + + return nil +} + +func (h *FromHeaders) setDefault() { + if h.Name == "" { + h.Name = "Authorization" + } + + if h.ValuePrefix == "" { + h.ValuePrefix = "Bearer " + } +} + +func (f *Filter) Config() interface{} { + return f.cfg +} diff --git a/pkg/pluginregistry/registry.go b/pkg/pluginregistry/registry.go index 6b01c4ec1..359f84d36 100644 --- a/pkg/pluginregistry/registry.go +++ b/pkg/pluginregistry/registry.go @@ -21,6 +21,7 @@ import ( _ "github.com/apache/dubbo-go-pixiu/pkg/adapter/dubboregistry" _ "github.com/apache/dubbo-go-pixiu/pkg/adapter/springcloud" _ "github.com/apache/dubbo-go-pixiu/pkg/filter/accesslog" + _ "github.com/apache/dubbo-go-pixiu/pkg/filter/auth/jwt" _ "github.com/apache/dubbo-go-pixiu/pkg/filter/authority" _ "github.com/apache/dubbo-go-pixiu/pkg/filter/cors" _ "github.com/apache/dubbo-go-pixiu/pkg/filter/csrf" diff --git a/samples/dubbogo/simple/jwt/pixiu/conf.yaml b/samples/dubbogo/simple/jwt/pixiu/conf.yaml new file mode 100644 index 000000000..343afb438 --- /dev/null +++ b/samples/dubbogo/simple/jwt/pixiu/conf.yaml @@ -0,0 +1,113 @@ +# +# 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. +# +--- +static_resources: + listeners: + - name: "net/http" + address: + socket_address: + protocol_type: "HTTP" + address: "0.0.0.0" + port: 8888 + filter_chains: + - filter_chain_match: + domains: + - api.dubbo.com + - api.pixiu.com + filters: + - name: dgp.filter.httpconnectionmanager + config: + route_config: + routes: + - match: + prefix: /health + route: + cluster: user + cluster_not_found_response_code: 505 + - match: + prefix: /user + route: + cluster: user + cluster_not_found_response_code: 505 + - match: + prefix: /prefix + route: + cluster: user + cluster_not_found_response_code: 505 + http_filters: + - name: dgp.filter.http.httpproxy + config: + - name: dgp.filter.http.auth.jwt + config: + rules: + - match: + prefix: /health + - match: + prefix: /user + requires: + requires_any: + provider_name: provider1 + - match: + prefix: /user/pixiu + requires: + requires_any: + provider_name: provider2 + - match: + prefix: /prefix + requires: + requires_all: + - provider_name: provider1 + - provider_name: provider2 + providers: + - name: provider1 + from_headers: + name: Authorization + value_prefix: "Bearer " + forward_payload_header: user-data + issuer: issuer1 + local_jwks: + inline_string: "{\"keys\":[{\"kty\":\"RSA\",\"e\":\"AQAB\",\"kid\":\"ee8d626d\",\"n\":\"gRda5b0pkgTytDuLrRnNSYhvfMIyM0ASq2ZggY4dVe12JV8N7lyXilyqLKleD-2lziivvzE8O8CdIC2vUf0tBD7VuMyldnZruSEZWCuKJPdgKgy9yPpShmD2NyhbwQIAbievGMJIp_JMwz8MkdY5pzhPECGNgCEtUAmsrrctP5V8HuxaxGt9bb-DdPXkYWXW3MPMSlVpGZ5GiIeTABxqYNG2MSoYeQ9x8O3y488jbassTqxExI_4w9MBQBJR9HIXjWrrrenCcDlMY71rzkbdj3mmcn9xMq2vB5OhfHyHTihbUPLSm83aFWSuW9lE7ogMc93XnrB8evIAk6VfsYlS9Q\"},{\"kty\":\"EC\",\"crv\":\"P-256\",\"kid\":\"711d48d1\",\"x\":\"tfXCoBU-wXemeQCkME1gMZWK0-UECCHIkedASZR0t-Q\",\"y\":\"9xzYtnKQdiQJHCtGwpZWF21eP1fy5x4wC822rCilmBw\"},{\"kty\":\"EC\",\"crv\":\"P-384\",\"kid\":\"d52c9829\",\"x\":\"tFx6ev6eLs9sNfdyndn4OgbhV6gPFVn7Ul0VD5vwuplJLbIYeFLI6T42tTaE5_Q4\",\"y\":\"A0gzB8TqxPX7xMzyHH_FXkYG2iROANH_kQxBovSeus6l_QSyqYlipWpBy9BhY9dz\"},{\"kty\":\"RSA\",\"e\":\"AQAB\",\"kid\":\"ecac72e5\",\"n\":\"nLbnTvZAUxdmuAbDDUNAfha6mw0fri3UpV2w1PxilflBuSnXJhzo532-YQITogoanMjy_sQ8kHUhZYHVRR6vLZRBBbl-hP8XWiCe4wwioy7Ey3TiIUYfW-SD6I42XbLt5o-47IR0j5YDXxnX2UU7-UgR_kITBeLDfk0rSp4B0GUhPbP5IDItS0MHHDDS3lhvJomxgEfoNrp0K0Fz_s0K33hfOqc2hD1tSkX-3oDTQVRMF4Nxax3NNw8-ahw6HNMlXlwWfXodgRMvj9pcz8xUYa3C5IlPlZkMumeNCFx1qds6K_eYcU0ss91DdbhhE8amRX1FsnBJNMRUkA5i45xkOIx15rQN230zzh0p71jvtx7wYRr5pdMlwxV0T9Ck5PCmx-GzFazA2X6DJ0Xnn1-cXkRoZHFj_8Mba1dUrNz-NWEk83uW5KT-ZEbX7nzGXtayKWmGb873a8aYPqIsp6bQ_-eRBd8TDT2g9HuPyPr5VKa1p33xKaohz4DGy3t1Qpy3UWnbPXUlh5dLWPKz-TcS9FP5gFhWVo-ZhU03Pn6P34OxHmXGWyQao18dQGqzgD4e9vY3rLhfcjVZJYNlWY2InsNwbYS-DnienPf1ws-miLeXxNKG3tFydoQzHwyOxG6Wc-HBfzL_hOvxINKQamvPasaYWl1LWznMps6elKCgKDc\"},{\"kty\":\"EC\",\"crv\":\"P-521\",\"kid\":\"c570888f\",\"x\":\"AHNpXq0J7rikNRlwhaMYDD8LGVAVJzNJ-jEPksUIn2LB2LCdNRzfAhgbxdQcWT9ktlc9M1EhmTLccEqfnWdGL9G1\",\"y\":\"AfHPUW3GYzzqbTczcYR0nYMVMFVrYsUxv4uiuSNV_XRN3Jf8zeYbbOLJv4S3bUytO7qHY8bfZxPxR9nn3BBTf5ol\"}]}" + - name: provider2 + from_headers: + name: Authorization + value_prefix: "Bearer " + forward_payload_header: user-data + issuer: issuer2 + remote_jwks: + http_uri: + uri: http://127.0.0.1:1314/remote + timeout: 5s + - name: dgp.filter.http.response + config: + config: + idle_timeout: 5s + read_timeout: 5s + write_timeout: 5s + clusters: + - name: "user" + lb_policy: "lb" + endpoints: + - id: 1 + socket_address: + address: 127.0.0.1 + port: 1314 + shutdown_config: + timeout: "60s" + step_timeout: "10s" + reject_policy: "immediacy" \ No newline at end of file diff --git a/samples/dubbogo/simple/jwt/pixiu/springcloud-conf.yaml b/samples/dubbogo/simple/jwt/pixiu/springcloud-conf.yaml new file mode 100644 index 000000000..4e0881c92 --- /dev/null +++ b/samples/dubbogo/simple/jwt/pixiu/springcloud-conf.yaml @@ -0,0 +1,99 @@ +# +# 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. +# +--- +static_resources: + listeners: + - name: "net/http" + address: + socket_address: + protocol_type: "HTTP" + address: "0.0.0.0" + port: 8888 + filter_chains: + - filter_chain_match: + domains: + - api.dubbo.com + - api.pixiu.com + filters: + - name: dgp.filter.httpconnectionmanager + config: + route_config: + dynamic: true + dynamic_apdter: "springcloud" + routes: + http_filters: + - name: dgp.filter.http.proxyrewrite + config: + uri_regex: + - "^/([^/]*)/(.*)$" + - "/$2" + - name: dgp.filter.http.auth.jwt + config: + rules: + - match: + prefix: /user-service/echo/Pixiu + requires: + requires_any: + provider_name: provider1 + - match: + prefix: /auth-service/echo/Pixiu + requires: + requires_all: + - provider_name: provider1 + - provider_name: provider2 + providers: + - name: provider1 + forward_payload_header: user-data + issuer: issuer1 + local_jwks: + inline_string: "{\"keys\":[{\"kty\":\"RSA\",\"e\":\"AQAB\",\"kid\":\"ee8d626d\",\"n\":\"gRda5b0pkgTytDuLrRnNSYhvfMIyM0ASq2ZggY4dVe12JV8N7lyXilyqLKleD-2lziivvzE8O8CdIC2vUf0tBD7VuMyldnZruSEZWCuKJPdgKgy9yPpShmD2NyhbwQIAbievGMJIp_JMwz8MkdY5pzhPECGNgCEtUAmsrrctP5V8HuxaxGt9bb-DdPXkYWXW3MPMSlVpGZ5GiIeTABxqYNG2MSoYeQ9x8O3y488jbassTqxExI_4w9MBQBJR9HIXjWrrrenCcDlMY71rzkbdj3mmcn9xMq2vB5OhfHyHTihbUPLSm83aFWSuW9lE7ogMc93XnrB8evIAk6VfsYlS9Q\"},{\"kty\":\"EC\",\"crv\":\"P-256\",\"kid\":\"711d48d1\",\"x\":\"tfXCoBU-wXemeQCkME1gMZWK0-UECCHIkedASZR0t-Q\",\"y\":\"9xzYtnKQdiQJHCtGwpZWF21eP1fy5x4wC822rCilmBw\"},{\"kty\":\"EC\",\"crv\":\"P-384\",\"kid\":\"d52c9829\",\"x\":\"tFx6ev6eLs9sNfdyndn4OgbhV6gPFVn7Ul0VD5vwuplJLbIYeFLI6T42tTaE5_Q4\",\"y\":\"A0gzB8TqxPX7xMzyHH_FXkYG2iROANH_kQxBovSeus6l_QSyqYlipWpBy9BhY9dz\"},{\"kty\":\"RSA\",\"e\":\"AQAB\",\"kid\":\"ecac72e5\",\"n\":\"nLbnTvZAUxdmuAbDDUNAfha6mw0fri3UpV2w1PxilflBuSnXJhzo532-YQITogoanMjy_sQ8kHUhZYHVRR6vLZRBBbl-hP8XWiCe4wwioy7Ey3TiIUYfW-SD6I42XbLt5o-47IR0j5YDXxnX2UU7-UgR_kITBeLDfk0rSp4B0GUhPbP5IDItS0MHHDDS3lhvJomxgEfoNrp0K0Fz_s0K33hfOqc2hD1tSkX-3oDTQVRMF4Nxax3NNw8-ahw6HNMlXlwWfXodgRMvj9pcz8xUYa3C5IlPlZkMumeNCFx1qds6K_eYcU0ss91DdbhhE8amRX1FsnBJNMRUkA5i45xkOIx15rQN230zzh0p71jvtx7wYRr5pdMlwxV0T9Ck5PCmx-GzFazA2X6DJ0Xnn1-cXkRoZHFj_8Mba1dUrNz-NWEk83uW5KT-ZEbX7nzGXtayKWmGb873a8aYPqIsp6bQ_-eRBd8TDT2g9HuPyPr5VKa1p33xKaohz4DGy3t1Qpy3UWnbPXUlh5dLWPKz-TcS9FP5gFhWVo-ZhU03Pn6P34OxHmXGWyQao18dQGqzgD4e9vY3rLhfcjVZJYNlWY2InsNwbYS-DnienPf1ws-miLeXxNKG3tFydoQzHwyOxG6Wc-HBfzL_hOvxINKQamvPasaYWl1LWznMps6elKCgKDc\"},{\"kty\":\"EC\",\"crv\":\"P-521\",\"kid\":\"c570888f\",\"x\":\"AHNpXq0J7rikNRlwhaMYDD8LGVAVJzNJ-jEPksUIn2LB2LCdNRzfAhgbxdQcWT9ktlc9M1EhmTLccEqfnWdGL9G1\",\"y\":\"AfHPUW3GYzzqbTczcYR0nYMVMFVrYsUxv4uiuSNV_XRN3Jf8zeYbbOLJv4S3bUytO7qHY8bfZxPxR9nn3BBTf5ol\"}]}" + - name: provider2 + forward_payload_header: user-data + issuer: issuer2 + remote_jwks: + http_uri: + uri: http://127.0.0.1:1314/remote + timeout: 5s + - name: dgp.filter.http.httpproxy + config: + - name: dgp.filter.http.response + config: + config: + idle_timeout: 5s + read_timeout: 5s + write_timeout: 5s + clusters: + shutdown_config: + timeout: "60s" + step_timeout: "10s" + reject_policy: "immediacy" + adapters: + - id: "springcloud" + name: "dgp.adapter.springcloud" + config: + freshInterval: 60s # 刷新配置时间 + # services: + # - user-service + # - auth-service + registry: + name: naocs + protocol: nacos + group: DEFAULT_GROUP + timeout: 3s + address: 127.0.0.1:8848 diff --git a/samples/dubbogo/simple/jwt/server/server.go b/samples/dubbogo/simple/jwt/server/server.go new file mode 100644 index 000000000..371004e22 --- /dev/null +++ b/samples/dubbogo/simple/jwt/server/server.go @@ -0,0 +1,47 @@ +/* + * 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 main + +import ( + "fmt" + "log" + "net/http" + "strings" +) + +func main() { + routers := []string{"/user", "/user/pixiu", "/prefix", "/health"} + + for _, router := range routers { + msg := router[strings.LastIndex(router, "/")+1:] + http.HandleFunc(router, func(w http.ResponseWriter, r *http.Request) { + _, _ = w.Write([]byte(fmt.Sprintf(`{"message":"%s","status":200}`, msg))) + }) + } + + remote() + + log.Println("Starting sample server ...") + log.Fatal(http.ListenAndServe(":1314", nil)) +} + +func remote() { + http.HandleFunc("/remote", func(w http.ResponseWriter, r *http.Request) { + _, _ = w.Write([]byte(`{"keys":[{"kty":"RSA","e":"AQAB","kid":"ee8d626d","n":"gRda5b0pkgTytDuLrRnNSYhvfMIyM0ASq2ZggY4dVe12JV8N7lyXilyqLKleD-2lziivvzE8O8CdIC2vUf0tBD7VuMyldnZruSEZWCuKJPdgKgy9yPpShmD2NyhbwQIAbievGMJIp_JMwz8MkdY5pzhPECGNgCEtUAmsrrctP5V8HuxaxGt9bb-DdPXkYWXW3MPMSlVpGZ5GiIeTABxqYNG2MSoYeQ9x8O3y488jbassTqxExI_4w9MBQBJR9HIXjWrrrenCcDlMY71rzkbdj3mmcn9xMq2vB5OhfHyHTihbUPLSm83aFWSuW9lE7ogMc93XnrB8evIAk6VfsYlS9Q"},{"kty":"EC","crv":"P-256","kid":"711d48d1","x":"tfXCoBU-wXemeQCkME1gMZWK0-UECCHIkedASZR0t-Q","y":"9xzYtnKQdiQJHCtGwpZWF21eP1fy5x4wC822rCilmBw"},{"kty":"EC","crv":"P-384","kid":"d52c9829","x":"tFx6ev6eLs9sNfdyndn4OgbhV6gPFVn7Ul0VD5vwuplJLbIYeFLI6T42tTaE5_Q4","y":"A0gzB8TqxPX7xMzyHH_FXkYG2iROANH_kQxBovSeus6l_QSyqYlipWpBy9BhY9dz"},{"kty":"RSA","e":"AQAB","kid":"ecac72e5","n":"nLbnTvZAUxdmuAbDDUNAfha6mw0fri3UpV2w1PxilflBuSnXJhzo532-YQITogoanMjy_sQ8kHUhZYHVRR6vLZRBBbl-hP8XWiCe4wwioy7Ey3TiIUYfW-SD6I42XbLt5o-47IR0j5YDXxnX2UU7-UgR_kITBeLDfk0rSp4B0GUhPbP5IDItS0MHHDDS3lhvJomxgEfoNrp0K0Fz_s0K33hfOqc2hD1tSkX-3oDTQVRMF4Nxax3NNw8-ahw6HNMlXlwWfXodgRMvj9pcz8xUYa3C5IlPlZkMumeNCFx1qds6K_eYcU0ss91DdbhhE8amRX1FsnBJNMRUkA5i45xkOIx15rQN230zzh0p71jvtx7wYRr5pdMlwxV0T9Ck5PCmx-GzFazA2X6DJ0Xnn1-cXkRoZHFj_8Mba1dUrNz-NWEk83uW5KT-ZEbX7nzGXtayKWmGb873a8aYPqIsp6bQ_-eRBd8TDT2g9HuPyPr5VKa1p33xKaohz4DGy3t1Qpy3UWnbPXUlh5dLWPKz-TcS9FP5gFhWVo-ZhU03Pn6P34OxHmXGWyQao18dQGqzgD4e9vY3rLhfcjVZJYNlWY2InsNwbYS-DnienPf1ws-miLeXxNKG3tFydoQzHwyOxG6Wc-HBfzL_hOvxINKQamvPasaYWl1LWznMps6elKCgKDc"},{"kty":"EC","crv":"P-521","kid":"c570888f","x":"AHNpXq0J7rikNRlwhaMYDD8LGVAVJzNJ-jEPksUIn2LB2LCdNRzfAhgbxdQcWT9ktlc9M1EhmTLccEqfnWdGL9G1","y":"AfHPUW3GYzzqbTczcYR0nYMVMFVrYsUxv4uiuSNV_XRN3Jf8zeYbbOLJv4S3bUytO7qHY8bfZxPxR9nn3BBTf5ol"}]}`)) + }) +} diff --git a/samples/dubbogo/simple/jwt/test/pixiu_test.go b/samples/dubbogo/simple/jwt/test/pixiu_test.go new file mode 100644 index 000000000..9f65f7409 --- /dev/null +++ b/samples/dubbogo/simple/jwt/test/pixiu_test.go @@ -0,0 +1,81 @@ +/* + * 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 jwt + +import ( + "io/ioutil" + "net/http" + "strings" + "testing" + "time" +) + +import ( + "github.com/stretchr/testify/assert" +) + +func TestLocalRemote(t *testing.T) { + client := &http.Client{Timeout: 5 * time.Second} + req, err := http.NewRequest(http.MethodGet, "http://localhost:1314/remote", nil) + assert.NoError(t, err) + resp, err := client.Do(req) + assert.NoError(t, err) + assert.Equal(t, http.StatusOK, resp.StatusCode) + assert.NotNil(t, resp) +} + +func TestLocal(t *testing.T) { + + verify(t, "http://localhost:8888/health", http.StatusUnauthorized) + + s := verify(t, "http://localhost:8888/user", http.StatusOK) + assert.True(t, strings.Contains(s, "user")) + + s = verify(t, "http://localhost:8888/user/pixiu", http.StatusOK) + assert.True(t, strings.Contains(s, "pixiu")) + + s = verify(t, "http://localhost:8888/prefix", http.StatusOK) + assert.True(t, strings.Contains(s, "prefix")) +} + +func TestRemote(t *testing.T) { + s := verify(t, "http://localhost:8888/prefix", http.StatusOK) + assert.True(t, strings.Contains(s, "prefix")) +} + +func TestSpringCloud(t *testing.T) { + s := verify(t, "http://localhost:8888/user-service/echo/Pixiu", http.StatusOK) + assert.True(t, strings.Contains(s, "User")) + s = verify(t, "http://localhost:8888/auth-service/echo/Pixiu", http.StatusOK) + assert.True(t, strings.Contains(s, "Auth")) +} + +func verify(t *testing.T, url string, status int) string { + var token = "eyJraWQiOiJlZThkNjI2ZCIsInR5cCI6IkpXVCIsImFsZyI6IlJTMjU2In0.eyJzdWIiOiJXZWlkb25nIiwiYXVkIjoiVGFzaHVhbiIsImlzcyI6Imp3a3Mtc2VydmljZS5hcHBzcG90LmNvbSIsImlhdCI6MTYzMTM2OTk1NSwianRpIjoiNDY2M2E5MTAtZWU2MC00NzcwLTgxNjktY2I3NDdiMDljZjU0In0.LwD65d5h6U_2Xco81EClMa_1WIW4xXZl8o4b7WzY_7OgPD2tNlByxvGDzP7bKYA9Gj--1mi4Q4li4CAnKJkaHRYB17baC0H5P9lKMPuA6AnChTzLafY6yf-YadA7DmakCtIl7FNcFQQL2DXmh6gS9J6TluFoCIXj83MqETbDWpL28o3XAD_05UP8VLQzH2XzyqWKi97mOuvz-GsDp9mhBYQUgN3csNXt2v2l-bUPWe19SftNej0cxddyGu06tXUtaS6K0oe0TTbaqc3hmfEiu5G0J8U6ztTUMwXkBvaknE640NPgMQJqBaey0E4u0txYgyvMvvxfwtcOrDRYqYPBnA" + client := &http.Client{Timeout: 5 * time.Second} + req, err := http.NewRequest(http.MethodGet, url, nil) + assert.NoError(t, err) + req.Header.Set("Authorization", "Bearer "+token) + resp, err := client.Do(req) + assert.NoError(t, err) + assert.Equal(t, status, resp.StatusCode) + assert.NotNil(t, resp) + s, _ := ioutil.ReadAll(resp.Body) + t.Log(string(s)) + return string(s) +}