mirror of
https://github.com/alibaba/higress.git
synced 2026-06-24 09:45:16 +08:00
A plugin that implements token parsing and authentication function based on wasm-go (#485)
This commit is contained in:
15
plugins/wasm-go/extensions/jwt-auth/README.md
Normal file
15
plugins/wasm-go/extensions/jwt-auth/README.md
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
# 功能说明
|
||||||
|
`jwt-auth`插件基于wasm-go实现了Token解析认证功能,可以判断Token是否有效,如果Token有效则继续访问后端微服务,Token无效或不存在直接拒绝并返回401
|
||||||
|
|
||||||
|
# 配置字段
|
||||||
|
| 名称 | 数据类型 | 填写要求 | 描述 |
|
||||||
|
| ------------ | ------------ | ------------ | ------------ |
|
||||||
|
| token_secret_key | string | 必填 | 配置Token解析使用的SecretKey|
|
||||||
|
| token_headers | string | 必填 | 配置获取Token请求头名称|
|
||||||
|
|
||||||
|
# 配置示例
|
||||||
|
```yaml
|
||||||
|
token_secret_key: Dav7kfq3iA8S!JUj8&CUkdnQe72E@Cw6
|
||||||
|
token_headers: token
|
||||||
|
```
|
||||||
|
此例`token_secret_key`中指定的是认证服务生成Token的SecretKey;`token_headers`是携带Token访问的请求头名称;
|
||||||
1
plugins/wasm-go/extensions/jwt-auth/VERSION
Normal file
1
plugins/wasm-go/extensions/jwt-auth/VERSION
Normal file
@@ -0,0 +1 @@
|
|||||||
|
1.0.0
|
||||||
18
plugins/wasm-go/extensions/jwt-auth/go.mod
Normal file
18
plugins/wasm-go/extensions/jwt-auth/go.mod
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
module jwt-auth
|
||||||
|
|
||||||
|
go 1.19
|
||||||
|
|
||||||
|
require (
|
||||||
|
github.com/alibaba/higress/plugins/wasm-go v0.0.0-20230811015533-49269b43032f
|
||||||
|
github.com/dgrijalva/jwt-go v3.2.0+incompatible
|
||||||
|
github.com/tetratelabs/proxy-wasm-go-sdk v0.22.0
|
||||||
|
github.com/tidwall/gjson v1.16.0
|
||||||
|
)
|
||||||
|
|
||||||
|
require (
|
||||||
|
github.com/google/uuid v1.3.0 // indirect
|
||||||
|
github.com/magefile/mage v1.14.0 // indirect
|
||||||
|
github.com/tidwall/match v1.1.1 // indirect
|
||||||
|
github.com/tidwall/pretty v1.2.0 // indirect
|
||||||
|
github.com/wasilibs/nottinygc v0.3.0 // indirect
|
||||||
|
)
|
||||||
22
plugins/wasm-go/extensions/jwt-auth/go.sum
Normal file
22
plugins/wasm-go/extensions/jwt-auth/go.sum
Normal file
@@ -0,0 +1,22 @@
|
|||||||
|
github.com/alibaba/higress/plugins/wasm-go v0.0.0-20230811015533-49269b43032f h1:H+2fEuroddobcGs2Vom+osc8CE3SBHLz+JbM036Lo9w=
|
||||||
|
github.com/alibaba/higress/plugins/wasm-go v0.0.0-20230811015533-49269b43032f/go.mod h1:shD9qvrDS6xklAVjKYho8kHIVdW4A1vhNEOAL2miEEE=
|
||||||
|
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
||||||
|
github.com/dgrijalva/jwt-go v3.2.0+incompatible h1:7qlOGliEKZXTDg6OTjfoBKDXWrumCAMpl/TFQ4/5kLM=
|
||||||
|
github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ=
|
||||||
|
github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I=
|
||||||
|
github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||||
|
github.com/magefile/mage v1.14.0 h1:6QDX3g6z1YvJ4olPhT1wksUcSa/V0a1B+pJb73fBjyo=
|
||||||
|
github.com/magefile/mage v1.14.0/go.mod h1:z5UZb/iS3GoOSn0JgWuiw7dxlurVYTu+/jHXqQg881A=
|
||||||
|
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||||
|
github.com/stretchr/testify v1.8.0 h1:pSgiaMZlXftHpm5L7V1+rVB+AZJydKsMxsQBIJw4PKk=
|
||||||
|
github.com/tetratelabs/proxy-wasm-go-sdk v0.22.0 h1:kS7BvMKN+FiptV4pfwiNX8e3q14evxAWkhYbxt8EI1M=
|
||||||
|
github.com/tetratelabs/proxy-wasm-go-sdk v0.22.0/go.mod h1:qkW5MBz2jch2u8bS59wws65WC+Gtx3x0aPUX5JL7CXI=
|
||||||
|
github.com/tidwall/gjson v1.16.0 h1:SyXa+dsSPpUlcwEDuKuEBJEz5vzTvOea+9rjyYodQFg=
|
||||||
|
github.com/tidwall/gjson v1.16.0/go.mod h1:/wbyibRr2FHMks5tjHJ5F8dMZh3AcwJEMf5vlfC0lxk=
|
||||||
|
github.com/tidwall/match v1.1.1 h1:+Ho715JplO36QYgwN9PGYNhgZvoUSc9X2c80KVTi+GA=
|
||||||
|
github.com/tidwall/match v1.1.1/go.mod h1:eRSPERbgtNPcGhD8UCthc6PmLEQXEWd3PRB5JTxsfmM=
|
||||||
|
github.com/tidwall/pretty v1.2.0 h1:RWIZEg2iJ8/g6fDDYzMpobmaoGh5OLl4AXtGUGPcqCs=
|
||||||
|
github.com/tidwall/pretty v1.2.0/go.mod h1:ITEVvHYasfjBbM0u2Pg8T2nJnzm8xPwvNhhsoaGGjNU=
|
||||||
|
github.com/wasilibs/nottinygc v0.3.0 h1:0L1jsJ1MsyN5tdinmFbLfuEA0TnHRcqaBM9pDTJVJmU=
|
||||||
|
github.com/wasilibs/nottinygc v0.3.0/go.mod h1:oDcIotskuYNMpqMF23l7Z8uzD4TC0WXHK8jetlB3HIo=
|
||||||
|
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
||||||
76
plugins/wasm-go/extensions/jwt-auth/main.go
Normal file
76
plugins/wasm-go/extensions/jwt-auth/main.go
Normal file
@@ -0,0 +1,76 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"github.com/alibaba/higress/plugins/wasm-go/pkg/wrapper"
|
||||||
|
jwt "github.com/dgrijalva/jwt-go"
|
||||||
|
"github.com/tetratelabs/proxy-wasm-go-sdk/proxywasm"
|
||||||
|
"github.com/tetratelabs/proxy-wasm-go-sdk/proxywasm/types"
|
||||||
|
"github.com/tidwall/gjson"
|
||||||
|
)
|
||||||
|
|
||||||
|
// 自定义插件配置
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
wrapper.SetCtx(
|
||||||
|
"jwt-auth", // 配置插件名称
|
||||||
|
wrapper.ParseConfigBy(parseConfig),
|
||||||
|
wrapper.ProcessRequestHeadersBy(onHttpRequestHeaders),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
type Config struct {
|
||||||
|
TokenSecretKey string // 解析Token SecretKey
|
||||||
|
TokenHeaders string // 定义获取Token请求头名称
|
||||||
|
}
|
||||||
|
|
||||||
|
type Res struct {
|
||||||
|
Code int `json:"code"` // 返回状态码
|
||||||
|
Msg string `json:"msg"` // 返回信息
|
||||||
|
}
|
||||||
|
|
||||||
|
func parseConfig(json gjson.Result, config *Config, log wrapper.Log) error {
|
||||||
|
// 解析出配置,更新到config中
|
||||||
|
config.TokenSecretKey = json.Get("token_secret_key").String()
|
||||||
|
config.TokenHeaders = json.Get("token_headers").String()
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func onHttpRequestHeaders(ctx wrapper.HttpContext, config Config, log wrapper.Log) types.Action {
|
||||||
|
var res Res
|
||||||
|
if config.TokenHeaders == "" || config.TokenSecretKey == "" {
|
||||||
|
res.Code = 401
|
||||||
|
res.Msg = "参数不足"
|
||||||
|
data, _ := json.Marshal(res)
|
||||||
|
_ = proxywasm.SendHttpResponse(401, nil, data, -1)
|
||||||
|
return types.ActionContinue
|
||||||
|
}
|
||||||
|
|
||||||
|
token, err := proxywasm.GetHttpRequestHeader(config.TokenHeaders)
|
||||||
|
if err != nil {
|
||||||
|
res.Code = 401
|
||||||
|
res.Msg = "认证失败"
|
||||||
|
data, _ := json.Marshal(res)
|
||||||
|
_ = proxywasm.SendHttpResponse(401, nil, data, -1)
|
||||||
|
return types.ActionContinue
|
||||||
|
}
|
||||||
|
valid := ParseTokenValid(token, config.TokenSecretKey)
|
||||||
|
if valid {
|
||||||
|
_ = proxywasm.ResumeHttpRequest()
|
||||||
|
return types.ActionPause
|
||||||
|
} else {
|
||||||
|
res.Code = 401
|
||||||
|
res.Msg = "认证失败"
|
||||||
|
data, _ := json.Marshal(res)
|
||||||
|
_ = proxywasm.SendHttpResponse(401, nil, data, -1)
|
||||||
|
return types.ActionContinue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func ParseTokenValid(tokenString, TokenSecretKey string) bool {
|
||||||
|
token, _ := jwt.Parse(tokenString, func(token *jwt.Token) (interface{}, error) {
|
||||||
|
// 在这里提供用于验证签名的密钥
|
||||||
|
return []byte(TokenSecretKey), nil
|
||||||
|
})
|
||||||
|
return token.Valid
|
||||||
|
}
|
||||||
59
test/e2e/conformance/tests/jwt-auth.go
Normal file
59
test/e2e/conformance/tests/jwt-auth.go
Normal file
@@ -0,0 +1,59 @@
|
|||||||
|
// Copyright (c) 2022 Alibaba Group Holding Ltd.
|
||||||
|
//
|
||||||
|
// Licensed 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 tests
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/alibaba/higress/test/e2e/conformance/utils/http"
|
||||||
|
"github.com/alibaba/higress/test/e2e/conformance/utils/suite"
|
||||||
|
)
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
HigressConformanceTests = append(HigressConformanceTests, WasmPluginsJwtAuth)
|
||||||
|
}
|
||||||
|
|
||||||
|
var WasmPluginsJwtAuth = suite.ConformanceTest{
|
||||||
|
ShortName: "WasmPluginsJwtAuth",
|
||||||
|
Description: "The Ingress in the higress-conformance-infra namespace test the jwt-auth wasmplugins.",
|
||||||
|
Manifests: []string{"tests/jwt-auth.yaml"},
|
||||||
|
Test: func(t *testing.T, suite *suite.ConformanceTestSuite) {
|
||||||
|
testcases := []http.Assertion{
|
||||||
|
{
|
||||||
|
Meta: http.AssertionMeta{
|
||||||
|
TargetBackend: "infra-backend-v1",
|
||||||
|
TargetNamespace: "higress-conformance-infra",
|
||||||
|
},
|
||||||
|
Request: http.AssertionRequest{
|
||||||
|
ActualRequest: http.Request{
|
||||||
|
Host: "foo.com",
|
||||||
|
Path: "/info",
|
||||||
|
UnfollowRedirect: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Response: http.AssertionResponse{
|
||||||
|
ExpectedResponse: http.Response{
|
||||||
|
StatusCode: 401,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
t.Run("WasmPlugins jwt-auth", func(t *testing.T) {
|
||||||
|
for _, testcase := range testcases {
|
||||||
|
http.MakeRequestAndExpectEventuallyConsistentResponse(t, suite.RoundTripper, suite.TimeoutConfig, suite.GatewayAddress, testcase)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
},
|
||||||
|
}
|
||||||
43
test/e2e/conformance/tests/jwt-auth.yaml
Normal file
43
test/e2e/conformance/tests/jwt-auth.yaml
Normal file
@@ -0,0 +1,43 @@
|
|||||||
|
# Copyright (c) 2022 Alibaba Group Holding Ltd.
|
||||||
|
#
|
||||||
|
# Licensed 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.
|
||||||
|
apiVersion: networking.k8s.io/v1
|
||||||
|
kind: Ingress
|
||||||
|
metadata:
|
||||||
|
annotations:
|
||||||
|
name: httproute-app-root
|
||||||
|
namespace: higress-conformance-infra
|
||||||
|
spec:
|
||||||
|
ingressClassName: higress
|
||||||
|
rules:
|
||||||
|
- host: "foo.com"
|
||||||
|
http:
|
||||||
|
paths:
|
||||||
|
- pathType: Prefix
|
||||||
|
path: "/"
|
||||||
|
backend:
|
||||||
|
service:
|
||||||
|
name: infra-backend-v1
|
||||||
|
port:
|
||||||
|
number: 8080
|
||||||
|
---
|
||||||
|
apiVersion: extensions.higress.io/v1alpha1
|
||||||
|
kind: WasmPlugin
|
||||||
|
metadata:
|
||||||
|
name: jwt-auth
|
||||||
|
namespace: higress-system
|
||||||
|
spec:
|
||||||
|
defaultConfig:
|
||||||
|
token_headers: token
|
||||||
|
token_secret_key: Dav7kfq3iA8S!JUj8&CUkdnQe72E@Cw6
|
||||||
|
url: file:///opt/plugins/wasm-go/extensions/jwt-auth/plugin.wasm
|
||||||
@@ -67,6 +67,7 @@ func TestHigressConformanceTests(t *testing.T) {
|
|||||||
} else {
|
} else {
|
||||||
higressTests = []suite.ConformanceTest{
|
higressTests = []suite.ConformanceTest{
|
||||||
tests.WasmPluginsRequestBlock,
|
tests.WasmPluginsRequestBlock,
|
||||||
|
tests.WasmPluginsJwtAuth,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
Reference in New Issue
Block a user