mirror of
https://github.com/eolinker/apinto
synced 2025-12-24 13:28:15 +08:00
应用初次提交
This commit is contained in:
@@ -1,6 +1,7 @@
|
||||
package application
|
||||
|
||||
import (
|
||||
"github.com/eolinker/eosc/eocontext"
|
||||
"github.com/eolinker/eosc/utils/config"
|
||||
)
|
||||
|
||||
@@ -14,6 +15,7 @@ func init() {
|
||||
}
|
||||
|
||||
type IApp interface {
|
||||
Auth(ctx eocontext.EoContext) error
|
||||
}
|
||||
|
||||
func CheckSkill(skill string) bool {
|
||||
|
||||
100
application/auth/aksk/aksk.go
Normal file
100
application/auth/aksk/aksk.go
Normal file
@@ -0,0 +1,100 @@
|
||||
package aksk
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"github.com/eolinker/eosc/utils/config"
|
||||
"time"
|
||||
|
||||
"github.com/eolinker/apinto/auth"
|
||||
"github.com/eolinker/eosc"
|
||||
http_service "github.com/eolinker/eosc/eocontext/http-context"
|
||||
)
|
||||
|
||||
//supportTypes 当前驱动支持的authorization type值
|
||||
var supportTypes = []string{
|
||||
"ak/sk",
|
||||
"aksk",
|
||||
}
|
||||
|
||||
type aksk struct {
|
||||
id string
|
||||
hideCredential bool
|
||||
users *akskUsers
|
||||
}
|
||||
|
||||
func (a *aksk) Id() string {
|
||||
return a.id
|
||||
}
|
||||
|
||||
func (a *aksk) Start() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (a *aksk) Reset(conf interface{}, workers map[eosc.RequireId]eosc.IWorker) error {
|
||||
c, ok := conf.(*Config)
|
||||
if !ok {
|
||||
return fmt.Errorf("need %s,now %s", config.TypeNameOf((*Config)(nil)), config.TypeNameOf(conf))
|
||||
}
|
||||
|
||||
a.hideCredential = c.HideCredentials
|
||||
|
||||
a.users = &akskUsers{
|
||||
users: c.Users,
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (a *aksk) Stop() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (a *aksk) CheckSkill(skill string) bool {
|
||||
return auth.CheckSkill(skill)
|
||||
}
|
||||
|
||||
func (a *aksk) Auth(context http_service.IHttpContext) error {
|
||||
authorizationType := context.Request().Header().GetHeader(auth.AuthorizationType)
|
||||
if authorizationType == "" {
|
||||
return auth.ErrorInvalidType
|
||||
}
|
||||
err := auth.CheckAuthorizationType(supportTypes, authorizationType)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
//解析Authorization字符串
|
||||
encType, ak, signHeaders, signature, err := parseAuthorization(context)
|
||||
//判断配置中是否存在该ak
|
||||
for _, user := range a.users.users {
|
||||
if ak == user.AK {
|
||||
switch encType {
|
||||
case "SDK-HMAC-SHA256", "HMAC-SHA256":
|
||||
{
|
||||
//结合context内的信息与配置的sk生成新的签名,与context携带的签名进行对比
|
||||
toSign := buildToSign(context, encType, signHeaders)
|
||||
s := hmaxBySHA256(user.SK, toSign)
|
||||
if s == signature {
|
||||
// 判断鉴权是否已过期
|
||||
if user.Expire != 0 && time.Now().Unix() > user.Expire {
|
||||
return errors.New("[ak/sk_auth] authorization expired")
|
||||
}
|
||||
|
||||
//若隐藏证书信息
|
||||
if a.hideCredential {
|
||||
context.Proxy().Header().DelHeader(auth.Authorization)
|
||||
}
|
||||
|
||||
//将label set进context
|
||||
for k, v := range user.Labels {
|
||||
context.SetLabel(k, v)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return errors.New("[ak/sk_auth] Invalid authorization")
|
||||
}
|
||||
153
application/auth/aksk/aksk_test.go
Normal file
153
application/auth/aksk/aksk_test.go
Normal file
@@ -0,0 +1,153 @@
|
||||
package aksk
|
||||
|
||||
import (
|
||||
"io"
|
||||
"net/http"
|
||||
"testing"
|
||||
|
||||
http_service "github.com/eolinker/eosc/eocontext/http-context"
|
||||
|
||||
"github.com/valyala/fasthttp"
|
||||
|
||||
http_context "github.com/eolinker/apinto/node/http-context"
|
||||
)
|
||||
|
||||
var akskConfig = []AKSKConfig{{
|
||||
AK: "4c897cfdfca60a59983adc2627942e7e",
|
||||
SK: "6bb8eee91f88336dd95b88a66709f0a3286ce1abf73453acc4619bc142d64040",
|
||||
Labels: map[string]string{},
|
||||
Expire: 1658740726, //2022-07-25 17:18:46
|
||||
}}
|
||||
|
||||
var testContexts = make([]http_service.IHttpContext, 0, 10)
|
||||
|
||||
func TestAKSK(t *testing.T) {
|
||||
testAKSK := &aksk{
|
||||
id: "123",
|
||||
hideCredential: true,
|
||||
users: &akskUsers{users: akskConfig},
|
||||
}
|
||||
|
||||
createTestContext()
|
||||
|
||||
err := testAKSK.Auth(testContexts[0])
|
||||
//if err != nil {
|
||||
// t.Errorf("测试1:预期是能够通过鉴权,结果是%s", err.Error())
|
||||
//}
|
||||
|
||||
err = testAKSK.Auth(testContexts[1])
|
||||
if err == nil {
|
||||
t.Errorf("测试2:预期是不能够通过鉴权%s:,结果是nil", err.Error())
|
||||
}
|
||||
|
||||
err = testAKSK.Auth(testContexts[2])
|
||||
if err == nil {
|
||||
t.Errorf("测试3:预期是不能够通过鉴权%s:,结果是nil", err.Error())
|
||||
}
|
||||
}
|
||||
|
||||
func createTestContext() {
|
||||
//使用正确sk加密后的签名
|
||||
|
||||
// http-service
|
||||
//request1, _ := http-service.NewRequest("GET", "http://www.demo.com/demo/login?parm1=value1&parm2=", &body{})
|
||||
//request1.IHeader.SetDriver("Authorization-Type", "ak/sk")
|
||||
//request1.IHeader.SetDriver("Content-Type", "application/json")
|
||||
//request1.IHeader.SetDriver("x-gateway-date", "20200605T104456Z")
|
||||
//request1.IHeader.SetDriver("Authorization", "HMAC-SHA256 Access=4c897cfdfca60a59983adc2627942e7e, SignedHeaders=content-type;host;x-gateway-date, Signature=0c3d2598d931f36ca7d261d52dcd29f09d6573671bd593b7cbc55f73eb942758")
|
||||
//Context1 := http_context.NewContext(request1, &writer{})
|
||||
|
||||
// fast http-service
|
||||
request1 := fasthttp.AcquireRequest()
|
||||
request1.SetRequestURI("http://www.demo.com/demo/login?parm1=value1&parm2=")
|
||||
request1.Header.SetMethod(fasthttp.MethodGet)
|
||||
request1.Header.Set("Authorization-Type", "ak/sk")
|
||||
request1.Header.Set("Content-Type", "application/json")
|
||||
request1.Header.Set("x-gateway-date", "20200605T104456Z")
|
||||
request1.Header.Set("Authorization", "HMAC-SHA256 Access=4c897cfdfca60a59983adc2627942e7e, SignedHeaders=content-type;host;x-gateway-date, Signature=0c3d2598d931f36ca7d261d52dcd29f09d6573671bd593b7cbc55f73eb942758")
|
||||
context1 := &fasthttp.RequestCtx{
|
||||
Request: *fasthttp.AcquireRequest(),
|
||||
Response: *fasthttp.AcquireResponse(),
|
||||
}
|
||||
request1.CopyTo(&context1.Request)
|
||||
|
||||
Context1 := http_context.NewContext(context1, 0)
|
||||
|
||||
testContexts = append(testContexts, Context1)
|
||||
|
||||
//使用错误sk加密后的签名
|
||||
|
||||
// http-service
|
||||
//request2, _ := http-service.NewRequest("GET", "http://www.demo.com/demo/login?parm1=value1&parm2=", &body{})
|
||||
//request2.IHeader.SetDriver("Authorization-Type", "ak/sk")
|
||||
//request2.IHeader.SetDriver("Content-Type", "application/json")
|
||||
//request2.IHeader.SetDriver("x-gateway-date", "20200605T104456Z")
|
||||
//request2.IHeader.SetDriver("Authorization", "HMAC-SHA256 Access=4c897cfdfca60a59983adc2627942e7e, SignedHeaders=content-type;host;x-gateway-date, Signature=bb18110ddf327a9c1222a551527896d59cb854ca9084078cfa3a6eb23de3ddb8")
|
||||
//Context2 := http_context.NewContext(request2, &writer{})
|
||||
|
||||
// https
|
||||
request2 := fasthttp.AcquireRequest()
|
||||
request2.SetRequestURI("http://www.demo.com/demo/login?parm1=value1&parm2=")
|
||||
request2.Header.SetMethod(fasthttp.MethodGet)
|
||||
request2.Header.Set("Authorization-Type", "ak/sk")
|
||||
request2.Header.Set("Content-Type", "application/json")
|
||||
request2.Header.Set("x-gateway-date", "20200605T104456Z")
|
||||
request2.Header.Set("Authorization", "HMAC-SHA256 Access=4c897cfdfca60a59983adc2627942e7e, SignedHeaders=content-type;host;x-gateway-date, Signature=bb18110ddf327a9c1222a551527896d59cb854ca9084078cfa3a6eb23de3ddb8")
|
||||
context2 := &fasthttp.RequestCtx{
|
||||
Request: *fasthttp.AcquireRequest(),
|
||||
Response: *fasthttp.AcquireResponse(),
|
||||
}
|
||||
request2.CopyTo(&context2.Request)
|
||||
|
||||
Context2 := http_context.NewContext(context2, 0)
|
||||
testContexts = append(testContexts, Context2)
|
||||
|
||||
//传输了不存在的ak
|
||||
// http-service
|
||||
//request3, _ := http-service.NewRequest("GET", "http://www.demo.com/demo/login?parm1=value1&parm2=", &body{})
|
||||
//request3.IHeader.SetDriver("Authorization-Type", "ak/sk")
|
||||
//request3.IHeader.SetDriver("Content-Type", "application/json")
|
||||
//request3.IHeader.SetDriver("x-gateway-date", "20200605T104456Z")
|
||||
//request3.IHeader.SetDriver("Authorization", "HMAC-SHA256 Access=dsaasdasda, SignedHeaders=content-type;host;x-gateway-date, Signature=0c3d2598d931f36ca7d261d52dcd29f09d6573671bd593b7cbc55f73eb942758")
|
||||
//Context3 := http_context.NewContext(request3, &writer{})
|
||||
//testContexts = append(testContexts, Context3)
|
||||
|
||||
// fast http-service
|
||||
request3 := fasthttp.AcquireRequest()
|
||||
request3.SetRequestURI("http://www.demo.com/demo/login?parm1=value1&parm2=")
|
||||
request3.Header.SetMethod(fasthttp.MethodGet)
|
||||
request3.Header.Set("Authorization-Type", "ak/sk")
|
||||
request3.Header.Set("Content-Type", "application/json")
|
||||
request3.Header.Set("x-gateway-date", "20200605T104456Z")
|
||||
request3.Header.Set("Authorization", "HMAC-SHA256 Access=dsaasdasda, SignedHeaders=content-type;host;x-gateway-date, Signature=0c3d2598d931f36ca7d261d52dcd29f09d6573671bd593b7cbc55f73eb942758")
|
||||
context3 := &fasthttp.RequestCtx{
|
||||
Request: *fasthttp.AcquireRequest(),
|
||||
Response: *fasthttp.AcquireResponse(),
|
||||
}
|
||||
request3.CopyTo(&context3.Request)
|
||||
Context3 := http_context.NewContext(context3, 0)
|
||||
testContexts = append(testContexts, Context3)
|
||||
}
|
||||
|
||||
type body struct {
|
||||
}
|
||||
|
||||
func (b body) Read(p []byte) (n int, err error) {
|
||||
return len(p), io.EOF
|
||||
}
|
||||
|
||||
type writer struct {
|
||||
}
|
||||
|
||||
func (w *writer) Header() http.Header {
|
||||
header := http.Header{}
|
||||
return header
|
||||
}
|
||||
|
||||
func (w *writer) Write(bytes []byte) (int, error) {
|
||||
return len(bytes), nil
|
||||
}
|
||||
|
||||
func (w *writer) WriteHeader(statusCode int) {
|
||||
return
|
||||
}
|
||||
17
application/auth/aksk/config.go
Normal file
17
application/auth/aksk/config.go
Normal file
@@ -0,0 +1,17 @@
|
||||
package aksk
|
||||
|
||||
type Config struct {
|
||||
HideCredentials bool `json:"hide_credentials" label:"是否隐藏证书"`
|
||||
Users []AKSKConfig `json:"user" label:"用户列表"`
|
||||
}
|
||||
|
||||
type akskUsers struct {
|
||||
users []AKSKConfig
|
||||
}
|
||||
|
||||
type AKSKConfig struct {
|
||||
AK string `json:"ak" label:"Access Key" nullable:"false"`
|
||||
SK string `json:"sk" label:"Secret Access Key" nullable:"false"`
|
||||
Labels map[string]string `json:"labels" label:"用户标签"`
|
||||
Expire int64 `json:"expire" format:"date-time" label:"过期时间"`
|
||||
}
|
||||
38
application/auth/aksk/driver.go
Normal file
38
application/auth/aksk/driver.go
Normal file
@@ -0,0 +1,38 @@
|
||||
package aksk
|
||||
|
||||
import (
|
||||
"reflect"
|
||||
|
||||
"github.com/eolinker/eosc"
|
||||
)
|
||||
|
||||
const (
|
||||
driverName = "aksk"
|
||||
)
|
||||
|
||||
//driver 实现github.com/eolinker/eosc.eosc.IProfessionDriver接口
|
||||
type driver struct {
|
||||
profession string
|
||||
name string
|
||||
driver string
|
||||
label string
|
||||
desc string
|
||||
configType reflect.Type
|
||||
}
|
||||
|
||||
//ConfigType 返回aksk鉴权驱动配置的反射类型
|
||||
func (d *driver) ConfigType() reflect.Type {
|
||||
return d.configType
|
||||
}
|
||||
|
||||
//Create 创建aksk鉴权驱动实例
|
||||
func (d *driver) Create(id, name string, v interface{}, workers map[eosc.RequireId]eosc.IWorker) (eosc.IWorker, error) {
|
||||
a := &aksk{
|
||||
id: id,
|
||||
}
|
||||
err := a.Reset(v, workers)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return a, nil
|
||||
}
|
||||
42
application/auth/aksk/factory.go
Normal file
42
application/auth/aksk/factory.go
Normal file
@@ -0,0 +1,42 @@
|
||||
package aksk
|
||||
|
||||
import (
|
||||
"github.com/eolinker/eosc"
|
||||
"github.com/eolinker/eosc/utils/schema"
|
||||
"reflect"
|
||||
)
|
||||
|
||||
var name = "auth_aksk"
|
||||
|
||||
//Register 注册aksk鉴权驱动工厂
|
||||
func Register(register eosc.IExtenderDriverRegister) {
|
||||
register.RegisterExtenderDriver(name, NewFactory())
|
||||
}
|
||||
|
||||
type factory struct {
|
||||
}
|
||||
|
||||
func (f *factory) Render() interface{} {
|
||||
render, err := schema.Generate(reflect.TypeOf((*Config)(nil)), nil)
|
||||
if err != nil {
|
||||
return nil
|
||||
}
|
||||
return render
|
||||
}
|
||||
|
||||
//NewFactory 创建aksk鉴权驱动工厂
|
||||
func NewFactory() eosc.IExtenderDriverFactory {
|
||||
return &factory{}
|
||||
}
|
||||
|
||||
//Create 创建aksk鉴权驱动
|
||||
func (f *factory) Create(profession string, name string, label string, desc string, params map[string]interface{}) (eosc.IExtenderDriver, error) {
|
||||
return &driver{
|
||||
profession: profession,
|
||||
name: name,
|
||||
label: label,
|
||||
desc: desc,
|
||||
driver: driverName,
|
||||
configType: reflect.TypeOf((*Config)(nil)),
|
||||
}, nil
|
||||
}
|
||||
124
application/auth/aksk/util.go
Normal file
124
application/auth/aksk/util.go
Normal file
@@ -0,0 +1,124 @@
|
||||
package aksk
|
||||
|
||||
import (
|
||||
"crypto/hmac"
|
||||
"crypto/sha256"
|
||||
"encoding/hex"
|
||||
"errors"
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
http_service "github.com/eolinker/eosc/eocontext/http-context"
|
||||
|
||||
"github.com/eolinker/apinto/auth"
|
||||
)
|
||||
|
||||
const dateHeader = "x-gateway-date"
|
||||
|
||||
//buildToSign 构建待加密的签名所需字符串
|
||||
func buildToSign(ctx http_service.IHttpContext, encType string, signedHeaders []string) string {
|
||||
toSign := strings.Builder{}
|
||||
toSign.WriteString(encType + "\n")
|
||||
dh := ctx.Request().Header().GetHeader(dateHeader)
|
||||
toSign.WriteString(dh + "\n")
|
||||
|
||||
cr := buildHexCanonicalRequest(ctx, signedHeaders)
|
||||
toSign.WriteString(strings.ToLower(cr))
|
||||
return toSign.String()
|
||||
}
|
||||
|
||||
//buildHexCanonicalRequest 构建规范消息头
|
||||
func buildHexCanonicalRequest(ctx http_service.IHttpContext, signedHeaders []string) string {
|
||||
cr := strings.Builder{}
|
||||
|
||||
cr.WriteString(strings.ToUpper(ctx.Request().Method()) + "\n")
|
||||
cr.WriteString(buildPath(ctx.Request().URI().Path()) + "\n")
|
||||
cr.WriteString(ctx.Request().URI().RawQuery() + "\n")
|
||||
|
||||
for _, header := range signedHeaders {
|
||||
if strings.ToLower(header) == "host" {
|
||||
cr.WriteString(buildHeaders(header, ctx.Request().Header().Host()) + "\n")
|
||||
continue
|
||||
}
|
||||
v := ctx.Request().Header().GetHeader(header)
|
||||
cr.WriteString(buildHeaders(header, v) + "\n")
|
||||
}
|
||||
cr.WriteString("\n")
|
||||
cr.WriteString(strings.Join(signedHeaders, ";") + "\n")
|
||||
body, _ := ctx.Request().Body().RawBody()
|
||||
cr.WriteString(hexEncode(body))
|
||||
|
||||
return hexEncode([]byte(cr.String()))
|
||||
}
|
||||
|
||||
func buildPath(path string) string {
|
||||
return strings.TrimSuffix(path, "/") + "/"
|
||||
}
|
||||
|
||||
func buildHeaders(hk, hv string) string {
|
||||
return fmt.Sprintf("%s:%s", hk, strings.TrimSpace(hv))
|
||||
}
|
||||
|
||||
func hexEncode(data []byte) string {
|
||||
sha := sha256.New()
|
||||
sha.Write(data)
|
||||
return hex.EncodeToString(sha.Sum(nil))
|
||||
}
|
||||
|
||||
func hmaxBySHA256(secretKey, toSign string) string {
|
||||
// 创建对应的sha256哈希加密算法
|
||||
hm := hmac.New(sha256.New, []byte(secretKey))
|
||||
//写入加密数据
|
||||
hm.Write([]byte(toSign))
|
||||
return hex.EncodeToString(hm.Sum(nil))
|
||||
}
|
||||
|
||||
func parseAuthorization(ctx http_service.IHttpContext) (encType string, accessKey string, signHeaders []string, signature string, err error) {
|
||||
authStr := ctx.Request().Header().GetHeader(auth.Authorization)
|
||||
|
||||
infos := strings.Split(authStr, ",")
|
||||
if len(infos) < 3 {
|
||||
err = errors.New("[ak/sk_auth] error authorization")
|
||||
return
|
||||
}
|
||||
encType, accessKey, err = parseAccessKey(infos[0])
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
signHeaders, err = parseSignHeaders(infos[1])
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
signature, err = parseSignature(infos[2])
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func parseAccessKey(info string) (string, string, error) {
|
||||
info = strings.TrimSpace(info)
|
||||
akInfos := strings.Split(info, " ")
|
||||
encType := ""
|
||||
accessKey := ""
|
||||
if len(akInfos) < 1 {
|
||||
return "", "", errors.New("[ak/sk_auth] error access key")
|
||||
} else if len(akInfos) == 1 {
|
||||
accessKey = strings.Replace(akInfos[0], "Access=", "", 1)
|
||||
} else if len(akInfos) == 2 {
|
||||
encType = akInfos[0]
|
||||
accessKey = strings.Replace(akInfos[1], "Access=", "", 1)
|
||||
}
|
||||
return encType, accessKey, nil
|
||||
}
|
||||
|
||||
func parseSignHeaders(info string) ([]string, error) {
|
||||
info = strings.Replace(strings.TrimSpace(info), "SignedHeaders=", "", 1)
|
||||
headers := strings.Split(strings.ToLower(info), ";")
|
||||
return headers, nil
|
||||
}
|
||||
|
||||
func parseSignature(info string) (string, error) {
|
||||
info = strings.Replace(strings.TrimSpace(info), "Signature=", "", 1)
|
||||
return info, nil
|
||||
}
|
||||
173
application/auth/apikey/apikey.go
Normal file
173
application/auth/apikey/apikey.go
Normal file
@@ -0,0 +1,173 @@
|
||||
package apikey
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"github.com/eolinker/eosc/utils/config"
|
||||
"reflect"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
http_service "github.com/eolinker/eosc/eocontext/http-context"
|
||||
|
||||
"github.com/eolinker/apinto/auth"
|
||||
"github.com/eolinker/eosc"
|
||||
)
|
||||
|
||||
//supportTypes 当前驱动支持的authorization type值
|
||||
var supportTypes = []string{
|
||||
"apikey",
|
||||
"apikey_auth",
|
||||
"apikey-auth",
|
||||
"apikeyauth",
|
||||
}
|
||||
|
||||
type apikey struct {
|
||||
id string
|
||||
hideCredential bool
|
||||
users *apiKeyUsers
|
||||
}
|
||||
|
||||
//Auth 鉴权处理
|
||||
func (a *apikey) Auth(ctx http_service.IHttpContext) error {
|
||||
authorizationType := ctx.Request().Header().GetHeader(auth.AuthorizationType)
|
||||
if authorizationType == "" {
|
||||
return auth.ErrorInvalidType
|
||||
}
|
||||
// 判断是否要鉴权要求
|
||||
err := auth.CheckAuthorizationType(supportTypes, authorizationType)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
authorization, err := a.getAuthValue(ctx)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
for _, user := range a.users.users {
|
||||
if authorization == user.Apikey {
|
||||
if user.Expire == 0 || time.Now().Unix() < user.Expire {
|
||||
//将label set进context
|
||||
for k, v := range user.Labels {
|
||||
ctx.SetLabel(k, v)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
return auth.ErrorExpireUser
|
||||
}
|
||||
}
|
||||
return auth.ErrorInvalidUser
|
||||
|
||||
}
|
||||
|
||||
//TOfData 获取数据的类型
|
||||
func TOfData(data interface{}) reflect.Kind {
|
||||
value := reflect.ValueOf(data)
|
||||
valueType := value.Kind()
|
||||
if valueType == reflect.Ptr {
|
||||
valueType = value.Elem().Kind()
|
||||
}
|
||||
return valueType
|
||||
}
|
||||
|
||||
//getAuthValue 获取Apikey值
|
||||
func (a *apikey) getAuthValue(ctx http_service.IHttpContext) (string, error) {
|
||||
// 判断鉴权值是否在header
|
||||
|
||||
if authorization := ctx.Proxy().Header().GetHeader(auth.Authorization); authorization != "" {
|
||||
if a.hideCredential {
|
||||
ctx.Proxy().Header().DelHeader(auth.Authorization)
|
||||
}
|
||||
return authorization, nil
|
||||
}
|
||||
|
||||
// 判断鉴权值是否在query
|
||||
if authorization := ctx.Proxy().URI().GetQuery("Apikey"); authorization != "" {
|
||||
if a.hideCredential {
|
||||
ctx.Proxy().URI().DelQuery("Apikey")
|
||||
|
||||
}
|
||||
return authorization, nil
|
||||
}
|
||||
var authorization string
|
||||
contentType := ctx.Request().Header().GetHeader("Content-Type")
|
||||
if strings.Contains(contentType, "application/x-www-form-urlencoded") || strings.Contains(contentType, "multipart/form-data") {
|
||||
formParams, err := ctx.Proxy().Body().BodyForm()
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
authorization = formParams.Get("Apikey")
|
||||
if a.hideCredential {
|
||||
delete(formParams, "Apikey")
|
||||
ctx.Proxy().Body().SetForm(formParams)
|
||||
}
|
||||
} else if strings.Contains(contentType, "application/json") {
|
||||
var body map[string]interface{}
|
||||
rawBody, err := ctx.Proxy().Body().RawBody()
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
if err = json.Unmarshal(rawBody, &body); err != nil {
|
||||
return "", err
|
||||
}
|
||||
if _, ok := body["Apikey"]; !ok {
|
||||
return "", errors.New("[apikey_auth] cannot find the Apikey in body")
|
||||
}
|
||||
if TOfData(body["Apikey"]) == reflect.String {
|
||||
authorization = body["Apikey"].(string)
|
||||
} else {
|
||||
return "", errors.New("[apikey_auth] Invalid data type for Apikey")
|
||||
}
|
||||
|
||||
if a.hideCredential {
|
||||
delete(body, "Apikey")
|
||||
newBody, err := json.Marshal(body)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
ctx.Proxy().Body().SetRaw(contentType, newBody)
|
||||
}
|
||||
|
||||
} else {
|
||||
return "", errors.New("[apikey_auth] Unsupported Content-Type")
|
||||
}
|
||||
|
||||
if authorization != "" {
|
||||
return authorization, nil
|
||||
}
|
||||
return "", errors.New("[apikey_auth] cannot find the Apikey in query/body/header")
|
||||
}
|
||||
|
||||
//Id 返回 worker ID
|
||||
func (a *apikey) Id() string {
|
||||
return a.id
|
||||
}
|
||||
|
||||
//Start
|
||||
func (a *apikey) Start() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
//Reset 重新加载配置
|
||||
func (a *apikey) Reset(conf interface{}, workers map[eosc.RequireId]eosc.IWorker) error {
|
||||
cfg, ok := conf.(*Config)
|
||||
if !ok {
|
||||
return fmt.Errorf("need %s,now %s", config.TypeNameOf((*Config)(nil)), config.TypeNameOf(conf))
|
||||
}
|
||||
a.users = &apiKeyUsers{
|
||||
users: cfg.User,
|
||||
}
|
||||
a.hideCredential = cfg.HideCredentials
|
||||
return nil
|
||||
}
|
||||
|
||||
//Stop
|
||||
func (a *apikey) Stop() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
//CheckSkill 技能检查
|
||||
func (a *apikey) CheckSkill(skill string) bool {
|
||||
return auth.CheckSkill(skill)
|
||||
}
|
||||
337
application/auth/apikey/apikey_test.go
Normal file
337
application/auth/apikey/apikey_test.go
Normal file
@@ -0,0 +1,337 @@
|
||||
package apikey
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"mime/multipart"
|
||||
|
||||
//"bytes"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
|
||||
"github.com/valyala/fasthttp"
|
||||
|
||||
//"mime/multipart"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/eolinker/apinto/auth"
|
||||
http_context "github.com/eolinker/apinto/node/http-context"
|
||||
)
|
||||
|
||||
var (
|
||||
users = []User{
|
||||
{
|
||||
Apikey: "asdqer",
|
||||
Labels: make(map[string]string),
|
||||
},
|
||||
{
|
||||
Apikey: "eolinker",
|
||||
Labels: make(map[string]string),
|
||||
Expire: 0,
|
||||
},
|
||||
{
|
||||
Apikey: "apinto",
|
||||
Labels: make(map[string]string),
|
||||
Expire: 1627013522,
|
||||
},
|
||||
}
|
||||
cfg = &Config{
|
||||
User: users,
|
||||
}
|
||||
)
|
||||
|
||||
func TestHeaderAuthorization(t *testing.T) {
|
||||
worker, err := getWorker("", "AuthorizationType")
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
return
|
||||
}
|
||||
headers := map[string]string{
|
||||
"authorization-type": "Apikey",
|
||||
"authorization": "eolinker",
|
||||
}
|
||||
// http-service
|
||||
//req, err := buildRequest(headers, nil, "")
|
||||
//if err != nil {
|
||||
// t.Error(err)
|
||||
// return
|
||||
//}
|
||||
//err = worker.Auth(http_context.NewContext(req, &writer{}))
|
||||
|
||||
// fast http-service
|
||||
req, err := buildFastRequest(headers, nil, "")
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
return
|
||||
}
|
||||
err = worker.Auth(http_context.NewContext(req, 0))
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
return
|
||||
}
|
||||
t.Log("auth success")
|
||||
return
|
||||
}
|
||||
func TestQueryAuthorization(t *testing.T) {
|
||||
worker, err := getWorker("", "AuthorizationType")
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
return
|
||||
}
|
||||
headers := map[string]string{
|
||||
"authorization-type": "Apikey",
|
||||
}
|
||||
query := map[string]string{
|
||||
"Apikey": "eolinker",
|
||||
}
|
||||
// http-service
|
||||
//req, err := buildRequest(headers, query, "")
|
||||
//if err != nil {
|
||||
// t.Error(err)
|
||||
// return
|
||||
//}
|
||||
//err = worker.Auth(http_context.NewContext(req, &writer{}))
|
||||
|
||||
// fast http-service
|
||||
req, err := buildFastRequest(headers, query, "")
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
return
|
||||
}
|
||||
err = worker.Auth(http_context.NewContext(req, 0))
|
||||
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
return
|
||||
}
|
||||
t.Log("auth success")
|
||||
return
|
||||
}
|
||||
func TestBodyAuthorization(t *testing.T) {
|
||||
var jsonBody = &struct {
|
||||
Apikey string
|
||||
}{
|
||||
Apikey: "eolinker",
|
||||
}
|
||||
|
||||
body, err := json.Marshal(jsonBody)
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
return
|
||||
}
|
||||
worker, err := getWorker("", "AuthorizationType")
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
return
|
||||
}
|
||||
headers := map[string]string{
|
||||
"authorization-type": "Apikey",
|
||||
"Content-Type": "application/json",
|
||||
}
|
||||
// http-service
|
||||
//req, err := http-service.NewRequest(http-service.MethodPost, "localhost:8081", bytes.NewReader(body))
|
||||
//if err != nil {
|
||||
// t.Error(err)
|
||||
// return
|
||||
//}
|
||||
//for key, value := range headers {
|
||||
// req.RequestHeader.SetDriver(key, value)
|
||||
//}
|
||||
//err = worker.Auth(http_context.NewContext(req, &writer{}))
|
||||
|
||||
// fast http-service
|
||||
req := fasthttp.AcquireRequest()
|
||||
req.SetRequestURI("localhost:8081")
|
||||
req.Header.SetMethod(fasthttp.MethodPost)
|
||||
req.SetBody(body)
|
||||
for key, value := range headers {
|
||||
req.Header.Set(key, value)
|
||||
}
|
||||
context := &fasthttp.RequestCtx{
|
||||
Request: *fasthttp.AcquireRequest(),
|
||||
Response: *fasthttp.AcquireResponse(),
|
||||
}
|
||||
req.CopyTo(&context.Request)
|
||||
err = worker.Auth(http_context.NewContext(context, 0))
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
return
|
||||
}
|
||||
t.Log("auth success")
|
||||
return
|
||||
}
|
||||
|
||||
func TestMultipartFormAuthorization(t *testing.T) {
|
||||
buf := new(bytes.Buffer)
|
||||
w := multipart.NewWriter(buf)
|
||||
err := w.WriteField("Apikey", "eolinker")
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
return
|
||||
}
|
||||
w.Close()
|
||||
worker, err := getWorker("", "AuthorizationType")
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
return
|
||||
}
|
||||
headers := map[string]string{
|
||||
"authorization-type": "Apikey",
|
||||
}
|
||||
// http-service
|
||||
//req, err := http-service.NewRequest(http-service.MethodPost, "localhost:8081", buf)
|
||||
//if err != nil {
|
||||
// t.Error(err)
|
||||
// return
|
||||
//}
|
||||
//for key, value := range headers {
|
||||
// req.RequestHeader.SetDriver(key, value)
|
||||
//}
|
||||
//req.RequestHeader.SetDriver("Content-Type", w.FormDataContentType())
|
||||
//err = worker.Auth(http_context.NewContext(req, &writer{}))
|
||||
|
||||
// fast http-service
|
||||
// fast http-service
|
||||
req := fasthttp.AcquireRequest()
|
||||
req.SetRequestURI("localhost:8081")
|
||||
req.Header.SetMethod(fasthttp.MethodPost)
|
||||
req.SetBodyString(buf.String())
|
||||
for key, value := range headers {
|
||||
req.Header.Set(key, value)
|
||||
}
|
||||
req.Header.Set("Content-Type", w.FormDataContentType())
|
||||
context := &fasthttp.RequestCtx{
|
||||
Request: *fasthttp.AcquireRequest(),
|
||||
Response: *fasthttp.AcquireResponse(),
|
||||
}
|
||||
req.CopyTo(&context.Request)
|
||||
err = worker.Auth(http_context.NewContext(context, 0))
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
return
|
||||
}
|
||||
t.Log("auth success")
|
||||
return
|
||||
}
|
||||
func TestFormAuthorization(t *testing.T) {
|
||||
var formBody = url.Values{
|
||||
"Apikey": []string{"eolinker"},
|
||||
}
|
||||
worker, err := getWorker("", "AuthorizationType")
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
return
|
||||
}
|
||||
headers := map[string]string{
|
||||
"authorization-type": "Apikey",
|
||||
"Content-Type": "application/x-www-form-urlencoded",
|
||||
}
|
||||
// http-service
|
||||
//req, err := buildRequest(headers, nil, formBody.encode())
|
||||
//if err != nil {
|
||||
// t.Error(err)
|
||||
// return
|
||||
//}
|
||||
//err = worker.Auth(http_context.NewContext(req, &writer{}))
|
||||
|
||||
// fast http-service
|
||||
req, err := buildFastRequest(headers, nil, formBody.Encode())
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
return
|
||||
}
|
||||
err = worker.Auth(http_context.NewContext(req, 0))
|
||||
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
return
|
||||
}
|
||||
t.Log("auth success")
|
||||
return
|
||||
}
|
||||
|
||||
func getWorker(id string, name string) (auth.IAuth, error) {
|
||||
f := NewFactory()
|
||||
driver, err := f.Create("auth", "apikey", "", "apikey驱动", nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
worker, err := driver.Create(id, name, cfg, nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
a, ok := worker.(auth.IAuth)
|
||||
if !ok {
|
||||
return nil, errors.New("invalid struct type")
|
||||
}
|
||||
return a, nil
|
||||
}
|
||||
|
||||
func buildRequest(headers map[string]string, query map[string]string, body string) (*http.Request, error) {
|
||||
method := http.MethodPost
|
||||
if len(query) > 0 {
|
||||
method = http.MethodGet
|
||||
}
|
||||
req, err := http.NewRequest(method, "localhost:8081", strings.NewReader(body))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
for key, value := range headers {
|
||||
req.Header.Set(key, value)
|
||||
}
|
||||
if len(query) > 0 {
|
||||
params := make(url.Values)
|
||||
for key, value := range query {
|
||||
params.Add(key, value)
|
||||
}
|
||||
req.URL.RawQuery = params.Encode()
|
||||
}
|
||||
return req, err
|
||||
}
|
||||
|
||||
func buildFastRequest(headers map[string]string, query map[string]string, body string) (*fasthttp.RequestCtx, error) {
|
||||
method := fasthttp.MethodPost
|
||||
if len(query) > 0 {
|
||||
method = fasthttp.MethodGet
|
||||
}
|
||||
req := fasthttp.AcquireRequest()
|
||||
req.SetRequestURI("localhost:8081")
|
||||
req.Header.SetMethod(method)
|
||||
req.SetBodyString(body)
|
||||
|
||||
for key, value := range headers {
|
||||
req.Header.Set(key, value)
|
||||
}
|
||||
|
||||
if len(query) > 0 {
|
||||
params := make(url.Values)
|
||||
for key, value := range query {
|
||||
params.Add(key, value)
|
||||
}
|
||||
req.URI().SetQueryString(params.Encode())
|
||||
}
|
||||
|
||||
context := &fasthttp.RequestCtx{
|
||||
Request: *fasthttp.AcquireRequest(),
|
||||
Response: *fasthttp.AcquireResponse(),
|
||||
}
|
||||
req.CopyTo(&context.Request)
|
||||
return context, nil
|
||||
}
|
||||
|
||||
type writer struct {
|
||||
}
|
||||
|
||||
func (w writer) Header() http.Header {
|
||||
panic("implement me")
|
||||
}
|
||||
|
||||
func (w writer) Write(bytes []byte) (int, error) {
|
||||
panic("implement me")
|
||||
}
|
||||
|
||||
func (w writer) WriteHeader(statusCode int) {
|
||||
panic("implement me")
|
||||
}
|
||||
18
application/auth/apikey/config.go
Normal file
18
application/auth/apikey/config.go
Normal file
@@ -0,0 +1,18 @@
|
||||
package apikey
|
||||
|
||||
//Config apiKey配置内容
|
||||
type Config struct {
|
||||
HideCredentials bool `json:"hide_credentials" label:"是否隐藏证书"`
|
||||
User []User `json:"user" label:"用户列表"`
|
||||
}
|
||||
|
||||
type apiKeyUsers struct {
|
||||
users []User
|
||||
}
|
||||
|
||||
//User 用户信息
|
||||
type User struct {
|
||||
Apikey string `json:"apikey" label:"密钥(Apikey)" nullable:"false"`
|
||||
Labels map[string]string `json:"labels" label:"用户标签"`
|
||||
Expire int64 `json:"expire" format:"date-time" label:"过期时间"`
|
||||
}
|
||||
38
application/auth/apikey/driver.go
Normal file
38
application/auth/apikey/driver.go
Normal file
@@ -0,0 +1,38 @@
|
||||
package apikey
|
||||
|
||||
import (
|
||||
"reflect"
|
||||
|
||||
"github.com/eolinker/eosc"
|
||||
)
|
||||
|
||||
const (
|
||||
driverName = "apikey"
|
||||
)
|
||||
|
||||
//driver 实现github.com/eolinker/eosc.eosc.IProfessionDriver接口
|
||||
type driver struct {
|
||||
profession string
|
||||
name string
|
||||
driver string
|
||||
label string
|
||||
desc string
|
||||
configType reflect.Type
|
||||
}
|
||||
|
||||
func (d *driver) ConfigType() reflect.Type {
|
||||
return d.configType
|
||||
}
|
||||
|
||||
func (d *driver) Create(id, name string, v interface{}, workers map[eosc.RequireId]eosc.IWorker) (eosc.IWorker, error) {
|
||||
|
||||
w := &apikey{
|
||||
id: id,
|
||||
}
|
||||
err := w.Reset(v, workers)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return w, nil
|
||||
}
|
||||
41
application/auth/apikey/factory.go
Normal file
41
application/auth/apikey/factory.go
Normal file
@@ -0,0 +1,41 @@
|
||||
package apikey
|
||||
|
||||
import (
|
||||
"github.com/eolinker/eosc"
|
||||
"github.com/eolinker/eosc/utils/schema"
|
||||
"reflect"
|
||||
)
|
||||
|
||||
var name = "auth_apikey"
|
||||
|
||||
//Register 注册auth驱动工厂
|
||||
func Register(register eosc.IExtenderDriverRegister) {
|
||||
register.RegisterExtenderDriver(name, NewFactory())
|
||||
}
|
||||
|
||||
type factory struct {
|
||||
}
|
||||
|
||||
//Create 创建apikey驱动
|
||||
func (f *factory) Create(profession string, name string, label string, desc string, params map[string]interface{}) (eosc.IExtenderDriver, error) {
|
||||
return &driver{
|
||||
profession: profession,
|
||||
name: name,
|
||||
label: label,
|
||||
desc: desc,
|
||||
driver: driverName,
|
||||
configType: reflect.TypeOf((*Config)(nil)),
|
||||
}, nil
|
||||
}
|
||||
func (f *factory) Render() interface{} {
|
||||
render, err := schema.Generate(reflect.TypeOf((*Config)(nil)), nil)
|
||||
if err != nil {
|
||||
return nil
|
||||
}
|
||||
return render
|
||||
}
|
||||
|
||||
//NewFactory 生成一个 auth_apiKey工厂
|
||||
func NewFactory() eosc.IExtenderDriverFactory {
|
||||
return &factory{}
|
||||
}
|
||||
103
application/auth/auth.go
Normal file
103
application/auth/auth.go
Normal file
@@ -0,0 +1,103 @@
|
||||
package auth
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
eoscContext "github.com/eolinker/eosc/eocontext"
|
||||
"github.com/eolinker/eosc/log"
|
||||
|
||||
"github.com/eolinker/eosc"
|
||||
)
|
||||
|
||||
var (
|
||||
ErrorInvalidAuth = errors.New("invalid auth")
|
||||
defaultAuthFactoryRegister = newAuthFactoryManager()
|
||||
)
|
||||
|
||||
//IAuthFactory 鉴权工厂方法
|
||||
type IAuthFactory interface {
|
||||
Create(driver string, config interface{}) (eoscContext.IAuthHandler, error)
|
||||
}
|
||||
|
||||
//IAuthFactoryRegister 实现了鉴权工厂管理器
|
||||
type IAuthFactoryRegister interface {
|
||||
RegisterFactoryByKey(key string, factory IAuthFactory)
|
||||
GetFactoryByKey(key string) (IAuthFactory, bool)
|
||||
Keys() []string
|
||||
}
|
||||
|
||||
//driverRegister 驱动注册器
|
||||
type driverRegister struct {
|
||||
register eosc.IRegister
|
||||
keys []string
|
||||
}
|
||||
|
||||
//newAuthFactoryManager 创建auth工厂管理器
|
||||
func newAuthFactoryManager() IAuthFactoryRegister {
|
||||
return &driverRegister{
|
||||
register: eosc.NewRegister(),
|
||||
keys: make([]string, 0, 10),
|
||||
}
|
||||
}
|
||||
|
||||
//GetFactoryByKey 获取指定auth工厂
|
||||
func (dm *driverRegister) GetFactoryByKey(key string) (IAuthFactory, bool) {
|
||||
log.Debug("GetFactoryByKey:", key)
|
||||
o, has := dm.register.Get(key)
|
||||
if has {
|
||||
log.Debug("GetFactoryByKey:", key, ":has")
|
||||
f, ok := o.(IAuthFactory)
|
||||
return f, ok
|
||||
}
|
||||
return nil, false
|
||||
}
|
||||
|
||||
//RegisterFactoryByKey 注册auth工厂
|
||||
func (dm *driverRegister) RegisterFactoryByKey(key string, factory IAuthFactory) {
|
||||
err := dm.register.Register(key, factory, true)
|
||||
log.Debug("RegisterFactoryByKey:", key)
|
||||
|
||||
if err != nil {
|
||||
log.Debug("RegisterFactoryByKey:", key, ":", err)
|
||||
return
|
||||
}
|
||||
dm.keys = append(dm.keys, key)
|
||||
}
|
||||
|
||||
//Keys 返回所有已注册的key
|
||||
func (dm *driverRegister) Keys() []string {
|
||||
return dm.keys
|
||||
}
|
||||
|
||||
//Register 注册auth工厂到默认auth工厂注册器
|
||||
func Register(key string, factory IAuthFactory) {
|
||||
|
||||
defaultAuthFactoryRegister.RegisterFactoryByKey(key, factory)
|
||||
}
|
||||
|
||||
//Get 从默认auth工厂注册器中获取auth工厂
|
||||
func Get(key string) (IAuthFactory, bool) {
|
||||
return defaultAuthFactoryRegister.GetFactoryByKey(key)
|
||||
}
|
||||
|
||||
//Keys 返回默认的auth工厂注册器中所有已注册的key
|
||||
func Keys() []string {
|
||||
return defaultAuthFactoryRegister.Keys()
|
||||
}
|
||||
|
||||
//GetFactory 获取指定auth工厂,若指定的不存在则返回一个已注册的工厂
|
||||
func GetFactory(name string) (IAuthFactory, error) {
|
||||
factory, ok := Get(name)
|
||||
if !ok {
|
||||
for _, key := range Keys() {
|
||||
factory, ok = Get(key)
|
||||
if ok {
|
||||
break
|
||||
}
|
||||
}
|
||||
if factory == nil {
|
||||
return nil, fmt.Errorf("%s:%w", name, ErrorInvalidAuth)
|
||||
}
|
||||
}
|
||||
return factory, nil
|
||||
}
|
||||
125
application/auth/basic/basic.go
Normal file
125
application/auth/basic/basic.go
Normal file
@@ -0,0 +1,125 @@
|
||||
package basic
|
||||
|
||||
import (
|
||||
"encoding/base64"
|
||||
"errors"
|
||||
"fmt"
|
||||
"github.com/eolinker/eosc/utils/config"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
http_service "github.com/eolinker/eosc/eocontext/http-context"
|
||||
|
||||
"github.com/eolinker/eosc"
|
||||
|
||||
"github.com/eolinker/apinto/auth"
|
||||
)
|
||||
|
||||
//supportTypes 当前驱动支持的authorization type值
|
||||
var supportTypes = []string{
|
||||
"basic",
|
||||
"basic_auth",
|
||||
"basic-auth",
|
||||
"basicauth",
|
||||
}
|
||||
|
||||
type basic struct {
|
||||
id string
|
||||
hideCredential bool
|
||||
users *basicUsers
|
||||
}
|
||||
|
||||
type basicUsers struct {
|
||||
users []User
|
||||
}
|
||||
|
||||
func (b *basicUsers) check(ctx http_service.IHttpContext, username string, password string) error {
|
||||
for _, u := range b.users {
|
||||
if u.Username == username && u.Password == password {
|
||||
if u.Expire == 0 || time.Now().Unix() < u.Expire {
|
||||
//将label set进context
|
||||
for k, v := range u.Labels {
|
||||
ctx.SetLabel(k, v)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
return auth.ErrorExpireUser
|
||||
}
|
||||
}
|
||||
return auth.ErrorInvalidUser
|
||||
}
|
||||
|
||||
func (b *basic) Id() string {
|
||||
return b.id
|
||||
}
|
||||
|
||||
func (b *basic) Start() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (b *basic) Reset(conf interface{}, workers map[eosc.RequireId]eosc.IWorker) error {
|
||||
cfg, ok := conf.(*Config)
|
||||
if !ok {
|
||||
return fmt.Errorf("need %s,now %s", config.TypeNameOf((*Config)(nil)), config.TypeNameOf(conf))
|
||||
}
|
||||
b.users = &basicUsers{
|
||||
cfg.User,
|
||||
}
|
||||
b.hideCredential = cfg.HideCredentials
|
||||
return nil
|
||||
}
|
||||
|
||||
func (b *basic) Stop() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (b *basic) CheckSkill(skill string) bool {
|
||||
return auth.CheckSkill(skill)
|
||||
}
|
||||
|
||||
func (b *basic) Auth(ctx http_service.IHttpContext) error {
|
||||
authorizationType := ctx.Request().Header().GetHeader(auth.AuthorizationType)
|
||||
if authorizationType == "" {
|
||||
return auth.ErrorInvalidType
|
||||
}
|
||||
err := auth.CheckAuthorizationType(supportTypes, authorizationType)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
authorization := ctx.Request().Header().GetHeader(auth.Authorization)
|
||||
if b.hideCredential {
|
||||
ctx.Proxy().Header().DelHeader(auth.Authorization)
|
||||
}
|
||||
|
||||
username, password, err := retrieveCredentials(authorization)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return b.users.check(ctx, username, password)
|
||||
}
|
||||
|
||||
//retrieveCredentials 获取basicAuth认证信息
|
||||
func retrieveCredentials(authInfo string) (string, string, error) {
|
||||
|
||||
if authInfo != "" {
|
||||
const basic = "basic"
|
||||
l := len(basic)
|
||||
|
||||
if len(authInfo) > l+1 && strings.ToLower(authInfo[:l]) == basic {
|
||||
b, err := base64.StdEncoding.DecodeString(authInfo[l+1:])
|
||||
if err != nil {
|
||||
return "", "", err
|
||||
}
|
||||
cred := string(b)
|
||||
for i := 0; i < len(cred); i++ {
|
||||
if cred[i] == ':' {
|
||||
return cred[:i], cred[i+1:], nil
|
||||
}
|
||||
}
|
||||
return "", "", errors.New("[basic_auth] header has unrecognized format")
|
||||
}
|
||||
return "", "", errors.New("[basic_auth] header has unrecognized format")
|
||||
}
|
||||
return "", "", errors.New("[basic_auth] authorization required")
|
||||
}
|
||||
224
application/auth/basic/basic_test.go
Normal file
224
application/auth/basic/basic_test.go
Normal file
@@ -0,0 +1,224 @@
|
||||
package basic
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"net/http"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/valyala/fasthttp"
|
||||
|
||||
"github.com/eolinker/apinto/auth"
|
||||
|
||||
http_context "github.com/eolinker/apinto/node/http-context"
|
||||
)
|
||||
|
||||
var (
|
||||
users = []User{
|
||||
{
|
||||
Username: "wu",
|
||||
Password: "123456",
|
||||
Expire: 1627009923,
|
||||
},
|
||||
{
|
||||
Username: "liu",
|
||||
Password: "123456",
|
||||
},
|
||||
{
|
||||
Username: "chen",
|
||||
Password: "123456",
|
||||
Expire: 1627013522,
|
||||
},
|
||||
}
|
||||
cfg = &Config{
|
||||
HideCredentials: true,
|
||||
User: users,
|
||||
}
|
||||
)
|
||||
|
||||
func getWorker(id string, name string) (auth.IAuth, error) {
|
||||
f := NewFactory()
|
||||
driver, err := f.Create("auth", "basic", "", "basic驱动", nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
worker, err := driver.Create(id, name, cfg, nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
a, ok := worker.(auth.IAuth)
|
||||
if !ok {
|
||||
return nil, errors.New("invalid struct type")
|
||||
}
|
||||
return a, nil
|
||||
}
|
||||
|
||||
func TestSuccessAuthorization(t *testing.T) {
|
||||
worker, err := getWorker("", "successAuthorization")
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
return
|
||||
}
|
||||
headers := map[string]string{
|
||||
"authorization-type": "basic",
|
||||
"authorization": "Basic bGl1OjEyMzQ1Ng==",
|
||||
}
|
||||
// http-service
|
||||
//req, err := buildRequest(headers)
|
||||
//err = worker.Auth(http_context.NewContext(req, &writer{}))
|
||||
//if err != nil {
|
||||
// t.Error(err)
|
||||
// return
|
||||
//}
|
||||
|
||||
// fast http-service
|
||||
req, err := buildFastRequest(headers)
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
return
|
||||
}
|
||||
err = worker.Auth(http_context.NewContext(req, 0))
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
return
|
||||
}
|
||||
|
||||
t.Log("auth success")
|
||||
return
|
||||
}
|
||||
|
||||
func TestExpireAuthorization(t *testing.T) {
|
||||
worker, err := getWorker("", "expireAuthorization")
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
return
|
||||
}
|
||||
headers := map[string]string{
|
||||
"authorization-type": "basic",
|
||||
"authorization": "Basic d3U6MTIzNDU2",
|
||||
}
|
||||
// http-service
|
||||
//req, err := buildRequest(headers)
|
||||
//if err != nil {
|
||||
// t.Error(err)
|
||||
// return
|
||||
//}
|
||||
//err = worker.Auth(http_context.NewContext(req, &writer{}))
|
||||
|
||||
// fast http-service
|
||||
req, err := buildFastRequest(headers)
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
return
|
||||
}
|
||||
err = worker.Auth(http_context.NewContext(req, 0))
|
||||
|
||||
if err == auth.ErrorExpireUser {
|
||||
t.Log("success")
|
||||
return
|
||||
}
|
||||
t.Error(err)
|
||||
return
|
||||
}
|
||||
|
||||
func TestNoAuthorization(t *testing.T) {
|
||||
worker, err := getWorker("", "noAuthorization")
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
return
|
||||
}
|
||||
headers := map[string]string{
|
||||
"authorization-type": "basic",
|
||||
}
|
||||
// http-service
|
||||
//req, err := buildRequest(headers)
|
||||
//if err != nil {
|
||||
// t.Error(err)
|
||||
// return
|
||||
//}
|
||||
//err = worker.Auth(http_context.NewContext(req, &writer{}))
|
||||
|
||||
// fast http-service
|
||||
req, err := buildFastRequest(headers)
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
return
|
||||
}
|
||||
err = worker.Auth(http_context.NewContext(req, 0))
|
||||
if err.Error() == "[basic_auth] authorization required" {
|
||||
t.Log("success")
|
||||
return
|
||||
}
|
||||
t.Error(err)
|
||||
return
|
||||
}
|
||||
|
||||
func TestNoAuthorizationType(t *testing.T) {
|
||||
worker, err := getWorker("", "noAuthorizationType")
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
return
|
||||
}
|
||||
// http-service
|
||||
//req, err := buildRequest(nil)
|
||||
//if err != nil {
|
||||
// t.Error(err)
|
||||
// return
|
||||
//}
|
||||
//err = worker.Auth(http_context.NewContext(req, &writer{}))
|
||||
|
||||
// fast http-service
|
||||
headers := map[string]string{}
|
||||
req, err := buildFastRequest(headers)
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
return
|
||||
}
|
||||
err = worker.Auth(http_context.NewContext(req, 0))
|
||||
if err == auth.ErrorInvalidType {
|
||||
t.Log("success")
|
||||
return
|
||||
}
|
||||
t.Error(err)
|
||||
return
|
||||
}
|
||||
|
||||
func buildRequest(headers map[string]string) (*http.Request, error) {
|
||||
req, err := http.NewRequest("POST", "localhost:8081", strings.NewReader(""))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
for key, value := range headers {
|
||||
req.Header.Set(key, value)
|
||||
}
|
||||
return req, err
|
||||
}
|
||||
|
||||
func buildFastRequest(headers map[string]string) (*fasthttp.RequestCtx, error) {
|
||||
context := &fasthttp.RequestCtx{
|
||||
Request: *fasthttp.AcquireRequest(),
|
||||
Response: *fasthttp.AcquireResponse(),
|
||||
}
|
||||
context.Request.Header.SetMethod(fasthttp.MethodPost)
|
||||
for key, value := range headers {
|
||||
context.Request.Header.Set(key, value)
|
||||
}
|
||||
context.Request.SetRequestURI("localhost:8081")
|
||||
return context, nil
|
||||
}
|
||||
|
||||
type writer struct {
|
||||
}
|
||||
|
||||
func (w writer) Header() http.Header {
|
||||
header := http.Header{}
|
||||
return header
|
||||
}
|
||||
|
||||
func (w writer) Write(bytes []byte) (int, error) {
|
||||
return len(bytes), nil
|
||||
}
|
||||
|
||||
func (w writer) WriteHeader(statusCode int) {
|
||||
return
|
||||
}
|
||||
15
application/auth/basic/config.go
Normal file
15
application/auth/basic/config.go
Normal file
@@ -0,0 +1,15 @@
|
||||
package basic
|
||||
|
||||
//Config basic配置内容
|
||||
type Config struct {
|
||||
HideCredentials bool `json:"hide_credentials" label:"是否隐藏证书"`
|
||||
User []User `json:"user" label:"用户列表"`
|
||||
}
|
||||
|
||||
//User 用户信息
|
||||
type User struct {
|
||||
Username string `json:"username" label:"用户名(username)" nullable:"false"`
|
||||
Password string `json:"password" label:"密码(password)" nullable:"false"`
|
||||
Labels map[string]string `json:"labels" label:"用户标签"`
|
||||
Expire int64 `json:"expire" format:"date-time" label:"过期时间"`
|
||||
}
|
||||
40
application/auth/basic/driver.go
Normal file
40
application/auth/basic/driver.go
Normal file
@@ -0,0 +1,40 @@
|
||||
package basic
|
||||
|
||||
import (
|
||||
"reflect"
|
||||
|
||||
"github.com/eolinker/eosc"
|
||||
)
|
||||
|
||||
const (
|
||||
driverName = "basic"
|
||||
)
|
||||
|
||||
//driver 实现github.com/eolinker/eosc.eosc.IProfessionDriver接口
|
||||
type driver struct {
|
||||
profession string
|
||||
name string
|
||||
|
||||
label string
|
||||
desc string
|
||||
configType reflect.Type
|
||||
}
|
||||
|
||||
//ConfigType 返回basic驱动配置的反射类型
|
||||
func (d *driver) ConfigType() reflect.Type {
|
||||
return d.configType
|
||||
}
|
||||
|
||||
//Create 创建http_proxy驱动的实例
|
||||
func (d *driver) Create(id, name string, v interface{}, workers map[eosc.RequireId]eosc.IWorker) (eosc.IWorker, error) {
|
||||
|
||||
w := &basic{
|
||||
id: id,
|
||||
}
|
||||
err := w.Reset(v, workers)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return w, nil
|
||||
}
|
||||
40
application/auth/basic/factory.go
Normal file
40
application/auth/basic/factory.go
Normal file
@@ -0,0 +1,40 @@
|
||||
package basic
|
||||
|
||||
import (
|
||||
"github.com/eolinker/eosc"
|
||||
"github.com/eolinker/eosc/utils/schema"
|
||||
"reflect"
|
||||
)
|
||||
|
||||
var name = "auth_basic"
|
||||
|
||||
//Register 注册http_proxy驱动工厂
|
||||
func Register(register eosc.IExtenderDriverRegister) {
|
||||
register.RegisterExtenderDriver(name, NewFactory())
|
||||
}
|
||||
|
||||
type factory struct {
|
||||
}
|
||||
|
||||
//NewFactory 创建http_proxy驱动工厂
|
||||
func NewFactory() eosc.IExtenderDriverFactory {
|
||||
return &factory{}
|
||||
}
|
||||
func (f *factory) Render() interface{} {
|
||||
render, err := schema.Generate(reflect.TypeOf((*Config)(nil)), nil)
|
||||
if err != nil {
|
||||
return nil
|
||||
}
|
||||
return render
|
||||
}
|
||||
|
||||
//Create 创建http_proxy驱动
|
||||
func (f *factory) Create(profession string, name string, label string, desc string, params map[string]interface{}) (eosc.IExtenderDriver, error) {
|
||||
return &driver{
|
||||
profession: profession,
|
||||
name: name,
|
||||
label: label,
|
||||
desc: desc,
|
||||
configType: reflect.TypeOf((*Config)(nil)),
|
||||
}, nil
|
||||
}
|
||||
22
application/auth/jwt/config.go
Normal file
22
application/auth/jwt/config.go
Normal file
@@ -0,0 +1,22 @@
|
||||
package jwt
|
||||
|
||||
//Config JWT实例配置
|
||||
type Config struct {
|
||||
Credentials []JwtCredential `json:"credentials" label:"证书列表"`
|
||||
SignatureIsBase64 bool `json:"signature_is_base64" label:"base64加密"`
|
||||
ClaimsToVerify []string `json:"claims_to_verify" label:"校验字段"`
|
||||
HideCredentials bool `json:"hide_credentials" label:"是否隐藏证书"`
|
||||
}
|
||||
|
||||
type jwtUsers struct {
|
||||
credentials []JwtCredential
|
||||
}
|
||||
|
||||
//JwtCredential JWT验证信息
|
||||
type JwtCredential struct {
|
||||
Iss string `json:"iss" label:"证书签发者" description:"playload计算内容之一"`
|
||||
Secret string `json:"secret" label:"密钥" description:"加密算法是HS时必填,用于校验token"`
|
||||
RSAPublicKey string `json:"rsa_public_key" label:"RSA公钥" description:"加密算法是RS或ES时必填,用于校验token"`
|
||||
Algorithm string `json:"algorithm" enum:"HS256,HS384,HS512,RS256,RS384,RS512,ES256,ES384,ES512" label:"签名算法"`
|
||||
Labels map[string]string `json:"labels" label:"用户标签"`
|
||||
}
|
||||
38
application/auth/jwt/driver.go
Normal file
38
application/auth/jwt/driver.go
Normal file
@@ -0,0 +1,38 @@
|
||||
package jwt
|
||||
|
||||
import (
|
||||
"reflect"
|
||||
|
||||
"github.com/eolinker/eosc"
|
||||
)
|
||||
|
||||
const (
|
||||
driverName = "jwt"
|
||||
)
|
||||
|
||||
//driver 实现github.com/eolinker/eosc.eosc.IProfessionDriver接口
|
||||
type driver struct {
|
||||
profession string
|
||||
name string
|
||||
driver string
|
||||
label string
|
||||
desc string
|
||||
configType reflect.Type
|
||||
}
|
||||
|
||||
//ConfigType 返回jwt鉴权驱动配置的反射类型
|
||||
func (d *driver) ConfigType() reflect.Type {
|
||||
return d.configType
|
||||
}
|
||||
|
||||
//Create 创建jwt鉴权驱动实例
|
||||
func (d *driver) Create(id, name string, v interface{}, workers map[eosc.RequireId]eosc.IWorker) (eosc.IWorker, error) {
|
||||
a := &jwt{
|
||||
id: id,
|
||||
}
|
||||
err := a.Reset(v, workers)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return a, nil
|
||||
}
|
||||
43
application/auth/jwt/factory.go
Normal file
43
application/auth/jwt/factory.go
Normal file
@@ -0,0 +1,43 @@
|
||||
package jwt
|
||||
|
||||
import (
|
||||
"github.com/eolinker/eosc/utils/schema"
|
||||
"reflect"
|
||||
|
||||
"github.com/eolinker/eosc"
|
||||
)
|
||||
|
||||
var name = "auth_jwt"
|
||||
|
||||
//Register 注册jwt鉴权驱动工厂
|
||||
func Register(register eosc.IExtenderDriverRegister) {
|
||||
register.RegisterExtenderDriver(name, NewFactory())
|
||||
}
|
||||
|
||||
type factory struct {
|
||||
}
|
||||
|
||||
func (f *factory) Render() interface{} {
|
||||
render, err := schema.Generate(reflect.TypeOf((*Config)(nil)), nil)
|
||||
if err != nil {
|
||||
return nil
|
||||
}
|
||||
return render
|
||||
}
|
||||
|
||||
//NewFactory 创建jwt鉴权驱动工厂
|
||||
func NewFactory() eosc.IExtenderDriverFactory {
|
||||
return &factory{}
|
||||
}
|
||||
|
||||
//Create 创建jwt鉴权驱动
|
||||
func (f *factory) Create(profession string, name string, label string, desc string, params map[string]interface{}) (eosc.IExtenderDriver, error) {
|
||||
return &driver{
|
||||
profession: profession,
|
||||
name: name,
|
||||
label: label,
|
||||
desc: desc,
|
||||
driver: driverName,
|
||||
configType: reflect.TypeOf((*Config)(nil)),
|
||||
}, nil
|
||||
}
|
||||
77
application/auth/jwt/jwt.go
Normal file
77
application/auth/jwt/jwt.go
Normal file
@@ -0,0 +1,77 @@
|
||||
package jwt
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/eolinker/eosc/utils/config"
|
||||
|
||||
http_service "github.com/eolinker/eosc/eocontext/http-context"
|
||||
|
||||
"github.com/eolinker/apinto/auth"
|
||||
"github.com/eolinker/eosc"
|
||||
)
|
||||
|
||||
var _ auth.IAuth = (*jwt)(nil)
|
||||
|
||||
//supportTypes 当前驱动支持的authorization type值
|
||||
var supportTypes = []string{
|
||||
"jwt",
|
||||
}
|
||||
|
||||
type jwt struct {
|
||||
id string
|
||||
credentials *jwtUsers
|
||||
signatureIsBase64 bool
|
||||
claimsToVerify []string
|
||||
hideCredentials bool
|
||||
}
|
||||
|
||||
func (j *jwt) Id() string {
|
||||
return j.id
|
||||
}
|
||||
|
||||
func (j *jwt) Start() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (j *jwt) Reset(conf interface{}, workers map[eosc.RequireId]eosc.IWorker) error {
|
||||
c, ok := conf.(*Config)
|
||||
if !ok {
|
||||
return fmt.Errorf("need %s,now %s", config.TypeNameOf((*Config)(nil)), config.TypeNameOf(conf))
|
||||
}
|
||||
|
||||
j.credentials = &jwtUsers{
|
||||
credentials: c.Credentials,
|
||||
}
|
||||
|
||||
j.signatureIsBase64 = c.SignatureIsBase64
|
||||
j.claimsToVerify = c.ClaimsToVerify
|
||||
j.hideCredentials = c.HideCredentials
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (j *jwt) Stop() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (j *jwt) CheckSkill(skill string) bool {
|
||||
return auth.CheckSkill(skill)
|
||||
}
|
||||
|
||||
func (j *jwt) Auth(context http_service.IHttpContext) error {
|
||||
authorizationType := context.Request().Header().GetHeader(auth.AuthorizationType)
|
||||
if authorizationType == "" {
|
||||
return auth.ErrorInvalidType
|
||||
}
|
||||
err := auth.CheckAuthorizationType(supportTypes, authorizationType)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
err = j.doJWTAuthentication(context)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
158
application/auth/jwt/jwt_test.go
Normal file
158
application/auth/jwt/jwt_test.go
Normal file
@@ -0,0 +1,158 @@
|
||||
package jwt
|
||||
|
||||
import (
|
||||
"io"
|
||||
"net/http"
|
||||
"testing"
|
||||
|
||||
"github.com/valyala/fasthttp"
|
||||
|
||||
http_context "github.com/eolinker/apinto/node/http-context"
|
||||
)
|
||||
|
||||
type responseWriter struct{}
|
||||
|
||||
func (w responseWriter) Header() http.Header {
|
||||
return http.Header(map[string][]string{})
|
||||
}
|
||||
func (w responseWriter) Write([]byte) (int, error) {
|
||||
return 0, nil
|
||||
}
|
||||
func (w responseWriter) WriteHeader(statusCode int) {
|
||||
}
|
||||
|
||||
type requestBody struct{}
|
||||
|
||||
func (r requestBody) Read(p []byte) (n int, err error) {
|
||||
return 0, io.EOF
|
||||
}
|
||||
|
||||
type JWTTest struct {
|
||||
testName string
|
||||
token string
|
||||
want string
|
||||
}
|
||||
|
||||
var JWTConf = &Config{
|
||||
SignatureIsBase64: false,
|
||||
ClaimsToVerify: []string{"exp", "nbf"},
|
||||
HideCredentials: true,
|
||||
Credentials: []JwtCredential{
|
||||
{Iss: "TestHS256", Secret: "eolinker", RSAPublicKey: "", Algorithm: "HS256"},
|
||||
{Iss: "TestHS384", Secret: "eolinker", RSAPublicKey: "", Algorithm: "HS384"},
|
||||
{Iss: "TestHS512", Secret: "eolinker", RSAPublicKey: "", Algorithm: "HS512"},
|
||||
{Iss: "TestRS256", Secret: "eolinker", RSAPublicKey: "-----BEGIN PUBLIC KEY-----\nMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAnzyis1ZjfNB0bBgKFMSv\nvkTtwlvBsaJq7S5wA+kzeVOVpVWwkWdVha4s38XM/pa/yr47av7+z3VTmvDRyAHc\naT92whREFpLv9cj5lTeJSibyr/Mrm/YtjCZVWgaOYIhwrXwKLqPr/11inWsAkfIy\ntvHWTxZYEcXLgAXFuUuaS3uF9gEiNQwzGTU1v0FqkqTBr4B8nW3HCN47XUu0t8Y0\ne+lf4s4OxQawWD79J9/5d3Ry0vbV3Am1FtGJiJvOwRsIfVChDpYStTcHTCMqtvWb\nV6L11BWkpzGXSW4Hv43qa+GSYOD2QU68Mb59oSk2OB+BtOLpJofmbGEGgvmwyCI9\nMwIDAQAB\n-----END PUBLIC KEY-----", Algorithm: "RS256"},
|
||||
{Iss: "TestRS384", Secret: "eolinker", RSAPublicKey: "-----BEGIN PUBLIC KEY-----\nMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAnzyis1ZjfNB0bBgKFMSv\nvkTtwlvBsaJq7S5wA+kzeVOVpVWwkWdVha4s38XM/pa/yr47av7+z3VTmvDRyAHc\naT92whREFpLv9cj5lTeJSibyr/Mrm/YtjCZVWgaOYIhwrXwKLqPr/11inWsAkfIy\ntvHWTxZYEcXLgAXFuUuaS3uF9gEiNQwzGTU1v0FqkqTBr4B8nW3HCN47XUu0t8Y0\ne+lf4s4OxQawWD79J9/5d3Ry0vbV3Am1FtGJiJvOwRsIfVChDpYStTcHTCMqtvWb\nV6L11BWkpzGXSW4Hv43qa+GSYOD2QU68Mb59oSk2OB+BtOLpJofmbGEGgvmwyCI9\nMwIDAQAB\n-----END PUBLIC KEY-----", Algorithm: "RS384"},
|
||||
{Iss: "TestRS512", Secret: "eolinker", RSAPublicKey: "-----BEGIN PUBLIC KEY-----\nMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAnzyis1ZjfNB0bBgKFMSv\nvkTtwlvBsaJq7S5wA+kzeVOVpVWwkWdVha4s38XM/pa/yr47av7+z3VTmvDRyAHc\naT92whREFpLv9cj5lTeJSibyr/Mrm/YtjCZVWgaOYIhwrXwKLqPr/11inWsAkfIy\ntvHWTxZYEcXLgAXFuUuaS3uF9gEiNQwzGTU1v0FqkqTBr4B8nW3HCN47XUu0t8Y0\ne+lf4s4OxQawWD79J9/5d3Ry0vbV3Am1FtGJiJvOwRsIfVChDpYStTcHTCMqtvWb\nV6L11BWkpzGXSW4Hv43qa+GSYOD2QU68Mb59oSk2OB+BtOLpJofmbGEGgvmwyCI9\nMwIDAQAB\n-----END PUBLIC KEY-----", Algorithm: "RS512"},
|
||||
{Iss: "TestES256", Secret: "eolinker", RSAPublicKey: "-----BEGIN PUBLIC KEY-----\nMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEEVs/o5+uQbTjL3chynL4wXgUg2R9\nq9UU8I5mEovUf86QZ7kOBIjJwqnzD1omageEHWwHdBO6B+dFabmdT9POxg==\n-----END PUBLIC KEY-----", Algorithm: "ES256"},
|
||||
{Iss: "TestES384", Secret: "eolinker", RSAPublicKey: "-----BEGIN PUBLIC KEY-----\nMHYwEAYHKoZIzj0CAQYFK4EEACIDYgAEC1uWSXj2czCDwMTLWV5BFmwxdM6PX9p+\nPk9Yf9rIf374m5XP1U8q79dBhLSIuaojsvOT39UUcPJROSD1FqYLued0rXiooIii\n1D3jaW6pmGVJFhodzC31cy5sfOYotrzF\n-----END PUBLIC KEY-----", Algorithm: "ES384"},
|
||||
{Iss: "TestES512", Secret: "eolinker", RSAPublicKey: "-----BEGIN PUBLIC KEY-----\nMIGbMBAGByqGSM49AgEGBSuBBAAjA4GGAAQBgc4HZz+/fBbC7lmEww0AO3NK9wVZ\nPDZ0VEnsaUFLEYpTzb90nITtJUcPUbvOsdZIZ1Q8fnbquAYgxXL5UgHMoywAib47\n6MkyyYgPk0BXZq3mq4zImTRNuaU9slj9TVJ3ScT3L1bXwVuPJDzpr5GOFpaj+WwM\nAl8G7CqwoJOsW7Kddns=\n-----END PUBLIC KEY-----", Algorithm: "ES512"},
|
||||
},
|
||||
}
|
||||
|
||||
var tests = []JWTTest{
|
||||
{
|
||||
testName: "测试HS256",
|
||||
token: "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyLCJpc3MiOiJUZXN0SFMyNTYiLCJuYmYiOjE2MjcyODkxNzUsImV4cCI6MTY1ODgyNTE3NX0.mOianLD1sBOVhJ9UZyrcQZgrBBBu9keviDRWydT6BXk",
|
||||
want: "nil",
|
||||
},
|
||||
{
|
||||
testName: "测试HS384",
|
||||
token: "eyJhbGciOiJIUzM4NCIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyLCJpc3MiOiJUZXN0SFMzODQiLCJuYmYiOjE2MjcyODkxNzUsImV4cCI6MTY1ODgyNTE3NX0.K5xuz653Vz3fu4m6BnUFUl6Me1H-fHDS5WxG4yyz8ZlgJnzvaSz9-rGnBdE0XYep",
|
||||
want: "nil",
|
||||
},
|
||||
{
|
||||
testName: "测试HS512",
|
||||
token: "eyJhbGciOiJIUzUxMiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyLCJpc3MiOiJUZXN0SFM1MTIiLCJuYmYiOjE2MjcyODkxNzUsImV4cCI6MTY1ODgyNTE3NX0.G7JqIKxEZOR6LzuUb_Epphgs9FINBokziQvdKg3CmkY8-QtGLk2YpMkCpsiRW18OTPYZq19iCjpYXQCYA1O3nw",
|
||||
want: "nil",
|
||||
},
|
||||
{
|
||||
testName: "测试RS256",
|
||||
token: "eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiYWRtaW4iOnRydWUsImlhdCI6MTUxNjIzOTAyMiwiaXNzIjoiVGVzdFJTMjU2IiwibmJmIjoxNjI3Mjg5MTc1LCJleHAiOjE2NTg4MjUxNzV9.HdIDSuzWhWqgI1mx4P1udieu77n3vysBC1xDT75ppafJjv7k-F9ihusIjqPNwtj82qVNtmoIgqfOJ4YHrOqmE3cRk1G99l7Tx93WtcHqsFEo5SuFElpt0dk4Yq3haSh65sR_LdqxP4-H7FvOaPrJ5Jrkq3G1WBMGrzblUA2WCUzCZ9ANhKsQjV8WE16Bh6I7oI759KCAvhtdsnpu4KERpuYbcrffaVfuYfzkjhrawqRjAFt7fA4hcq1b9srEqmXxUc9IQ0AYmUWn9yyvNgdXGa-GrioT4Up7CYpmp5lbnjwgLo3cH7kDh5iyj-evVmOsyLvE2Ha9ZC-iV7wThyyE6g",
|
||||
want: "nil",
|
||||
},
|
||||
{
|
||||
testName: "测试RS384",
|
||||
token: "eyJhbGciOiJSUzM4NCIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiYWRtaW4iOnRydWUsImlhdCI6MTUxNjIzOTAyMiwiaXNzIjoiVGVzdFJTMzg0IiwibmJmIjoxNjI3Mjg5MTc1LCJleHAiOjE2NTg4MjUxNzV9.J15a16W4S-uLwWMJ9M1cpw055u82APrNX0XARf4od4bjJl3dLdmj797bYcdOMn13NLw3y6KAftY5iDrBjIBiirMg4X0bHCrc1FrenA9vfb7lzhWztiIdkrqv1zs-nV21JVgMhYkpwBF99BDdHNTuzLWyTob0s0z1VHcRkAFvEjttBmPnlLeX_NrSwho-hPolybycvFfBJaoZ1qjtmYwVYQMt-0TVnet_jxhbPCvSBzNl-6wM6cmD5PvSJXI_tvO8TtxHuV5KLh7p1IVgVVwTURGffzIo_KcdcofM_NnQkNIl6x3yuGNkB8pFAhMDDYVQjcW2zoTzdtLBqad-UQFfLA",
|
||||
want: "nil",
|
||||
},
|
||||
{
|
||||
testName: "测试RS512",
|
||||
token: "eyJhbGciOiJSUzUxMiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiYWRtaW4iOnRydWUsImlhdCI6MTUxNjIzOTAyMiwiaXNzIjoiVGVzdFJTNTEyIiwibmJmIjoxNjI3Mjg5MTc1LCJleHAiOjE2NTg4MjUxNzV9.IpkdhYcUlAZb_nAPDI8sFCkb07TQ4wZ8e-HIZvlmzdKszl02mEaJXt9OgusVS5hZ3vohQ6abpxBEu0B89MzsJIbanH6Rz3oxmA-R837ac_6dEPHHOVTHbUawQ3qfa0YCb5HXQ2X4ldu-cmxlUeWi2ZTDOQU4bT7GoBneC8X-ae7c5zt71juIUlg0JxU53YLN93POqpWcNneJIGoC9VyX0NKm55eNp96yfXXWXaOhXxOXaZyeW4ACZ6GFu5H9BKkiG_tZjzA5Pppg5YxpsIpKrdEoTUnPpmoBCUWEsMc1T6KyoHAjIAXAgiNxLe3Q5Fgsf-789RSpzk1VeWetJkwGCA",
|
||||
want: "nil",
|
||||
},
|
||||
{
|
||||
testName: "测试ES256",
|
||||
token: "eyJhbGciOiJFUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiYWRtaW4iOnRydWUsImlhdCI6MTUxNjIzOTAyMiwiaXNzIjoiVGVzdEVTMjU2IiwibmJmIjoxNjI3Mjg5MTc1LCJleHAiOjE2NTg4MjUxNzV9.4XAE1wCY_5P7qGDOq04DfJWzgRJl28W1fN-Jh7a5sXj9ylYwRh5J35yoPz0lCfXAgt_hT_UjCAlB2h9OALKUGQ",
|
||||
want: "nil",
|
||||
},
|
||||
{
|
||||
testName: "测试ES384",
|
||||
token: "eyJhbGciOiJFUzM4NCIsInR5cCI6IkpXVCIsImtpZCI6ImlUcVhYSTB6YkFuSkNLRGFvYmZoa00xZi02ck1TcFRmeVpNUnBfMnRLSTgifQ.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiYWRtaW4iOnRydWUsImlhdCI6MTUxNjIzOTAyMiwiaXNzIjoiVGVzdEVTMzg0IiwibmJmIjoxNjI3Mjg5MTc1LCJleHAiOjE2NTg4MjUxNzV9.Mt0ZXHkDM50vx1nEjJs2Ok1HnE-64kFAp4j_xGNbL0q9wWq2u6n1AhbcDGa0v6GuqWuV1vrMStUR1t-5OhYJeQxyMHNjLqxyD6GJ8THODXUDIy81Nt1zN-qpjj6H7JeC",
|
||||
want: "nil",
|
||||
},
|
||||
{
|
||||
testName: "测试ES512",
|
||||
token: "eyJhbGciOiJFUzUxMiIsInR5cCI6IkpXVCIsImtpZCI6InhaRGZacHJ5NFA5dlpQWnlHMmZOQlJqLTdMejVvbVZkbTd0SG9DZ1NOZlkifQ.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiYWRtaW4iOnRydWUsImlhdCI6MTUxNjIzOTAyMiwiaXNzIjoiVGVzdEVTNTEyIiwibmJmIjoxNjI3Mjg5MTc1LCJleHAiOjE2NTg4MjUxNzV9.AZZdNIQ_bhhMCRcjoz4exemQ2yXvtRgKp27Oc3JbrGNSTOYbxBQpHJ79zfRjdGIM6Un6cgAeJgwTsYberuun-ccDACiiWgKbER3qVssz5bG4rb7eiEHhTaRF5b0gqaDdQE3i3QWdJMxp15-QrJbOQawr2DV4stUAjiJkEFzCej3asEvd",
|
||||
want: "nil",
|
||||
},
|
||||
{
|
||||
testName: "测试HS512过期的token",
|
||||
token: "eyJhbGciOiJIUzUxMiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyLCJpc3MiOiJUZXN0SFM1MTIiLCJuYmYiOjE2MjcyODkxNzUsImV4cCI6MTYyNzI4OTIzNX0.5oUtf-royNU8ckXgaDDVCv5dpVVdFyL7fOVz57LaMHXlzRyPQfLNGLMeaIWY6ZUpqRbzT_Jm3OZMXs5MVRmZlw",
|
||||
want: "错误",
|
||||
},
|
||||
{
|
||||
testName: "测试HS512未到开始时间nbf的token",
|
||||
token: "eyJhbGciOiJIUzUxMiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyLCJpc3MiOiJUZXN0SFM1MTIiLCJuYmYiOjE2MzcyODkxNzUsImV4cCI6MTYzNzI4OTIzNX0.mHTikcPyIHUAVBaHBHHTmpORcZQrZOqFWJPBR9by4mvVpJ73_WNchd093_yDs61Eh0LHs44ww047k-S4s5KrVA",
|
||||
want: "错误",
|
||||
},
|
||||
{
|
||||
testName: "测试HS256ISS不存在的token",
|
||||
token: "eyJhbGciOiJIUzUxMiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyLCJpc3MiOiJUZXN0SFN4eHgiLCJuYmYiOjE2MjcyODkyMzUsImV4cCI6MTY1ODgyNTIzNX0.42_GWGNLlHG1K-bCrB-NI0fNLEa9F6LBcX-pfdQWUn1RE0nB0SewDI_PyA62NtnMOc_R5rMBHcwdW5WUCxiPVw",
|
||||
want: "错误",
|
||||
},
|
||||
}
|
||||
|
||||
func TestJWT(t *testing.T) {
|
||||
jwtMoudule := &jwt{}
|
||||
err := jwtMoudule.Reset(JWTConf, nil)
|
||||
if err != nil {
|
||||
t.Errorf("配置读取出错 err:%s", err)
|
||||
}
|
||||
|
||||
for _, test := range tests {
|
||||
t.Run(test.testName, func(t *testing.T) {
|
||||
// http-service
|
||||
//自己造formData, query或者body内插入jwt_token
|
||||
//httpRequest, _ := http-service.NewRequest("GET", "/asd/asd/asd?jwt_token="+test.token, requestBody{})
|
||||
//httpRequest.RequestHeader.SetDriver("Content-Type", "multipart/form-data")
|
||||
//ctx := http_context.NewContext(httpRequest, responseWriter{})
|
||||
//ctx.RequestOrg.SetHeader("Authorization-Type", "Jwt")
|
||||
|
||||
// fasthttp
|
||||
context := &fasthttp.RequestCtx{
|
||||
Request: *fasthttp.AcquireRequest(),
|
||||
Response: *fasthttp.AcquireResponse(),
|
||||
}
|
||||
context.Request.Header.SetMethod(fasthttp.MethodGet)
|
||||
context.Request.Header.Set("Content-Type", "multipart/form-data")
|
||||
context.Request.SetRequestURI("/asd/asd/asd?jwt_token=" + test.token)
|
||||
ctx := http_context.NewContext(context, 0)
|
||||
|
||||
err = jwtMoudule.Auth(ctx)
|
||||
|
||||
resultErr := ""
|
||||
if err != nil {
|
||||
resultErr = "错误"
|
||||
} else {
|
||||
resultErr = "nil"
|
||||
}
|
||||
|
||||
if resultErr != test.want {
|
||||
t.Errorf("test %s Fail", test.testName)
|
||||
}
|
||||
|
||||
})
|
||||
}
|
||||
|
||||
}
|
||||
615
application/auth/jwt/verify.go
Normal file
615
application/auth/jwt/verify.go
Normal file
@@ -0,0 +1,615 @@
|
||||
package jwt
|
||||
|
||||
import (
|
||||
"crypto"
|
||||
"crypto/ecdsa"
|
||||
"crypto/hmac"
|
||||
"crypto/rand"
|
||||
"crypto/rsa"
|
||||
"crypto/x509"
|
||||
"encoding/base64"
|
||||
"encoding/json"
|
||||
"encoding/pem"
|
||||
"errors"
|
||||
"math/big"
|
||||
"reflect"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
http_service "github.com/eolinker/eosc/eocontext/http-context"
|
||||
)
|
||||
|
||||
type jwtToken struct {
|
||||
Token string
|
||||
Header64 string
|
||||
Claims64 string
|
||||
Signature64 string
|
||||
Header map[string]interface{}
|
||||
Claims map[string]interface{}
|
||||
Signature string
|
||||
}
|
||||
|
||||
type signingMethod struct {
|
||||
Name string
|
||||
Hash crypto.Hash
|
||||
KeySize int
|
||||
CurveBits int
|
||||
}
|
||||
|
||||
var (
|
||||
errInvalidKey = errors.New("key is invalid")
|
||||
errInvalidKeyType = errors.New("key is of invalid type")
|
||||
errHashUnavailable = errors.New("the requested hash function is unavailable")
|
||||
errSignatureInvalid = errors.New("signature is invalid")
|
||||
errInvalidSigningMethod = errors.New("signing method is invalid")
|
||||
errECDSAVerification = errors.New("crypto/ecdsa: verification error")
|
||||
errKeyMustBePEMEncoded = errors.New("Invalid Key: Key must be PEM encoded PKCS1 or PKCS8 private key")
|
||||
errNotRSAPublicKey = errors.New("Key is not a valid RSA public key")
|
||||
errNotECPublicKey = errors.New("Key is not a valid ECDSA public key")
|
||||
)
|
||||
|
||||
func newSigningMethod(name string) *signingMethod {
|
||||
switch name {
|
||||
case "HS256":
|
||||
return &signingMethod{Name: name, Hash: crypto.SHA256}
|
||||
case "HS384":
|
||||
return &signingMethod{Name: name, Hash: crypto.SHA384}
|
||||
case "HS512":
|
||||
return &signingMethod{Name: name, Hash: crypto.SHA512}
|
||||
case "RS256":
|
||||
return &signingMethod{Name: name, Hash: crypto.SHA256}
|
||||
case "RS384":
|
||||
return &signingMethod{Name: name, Hash: crypto.SHA384}
|
||||
case "RS512":
|
||||
return &signingMethod{Name: name, Hash: crypto.SHA512}
|
||||
case "ES256":
|
||||
return &signingMethod{Name: name, Hash: crypto.SHA256, KeySize: 32, CurveBits: 256}
|
||||
case "ES384":
|
||||
return &signingMethod{Name: name, Hash: crypto.SHA384, KeySize: 48, CurveBits: 384}
|
||||
case "ES512":
|
||||
return &signingMethod{Name: name, Hash: crypto.SHA512, KeySize: 66, CurveBits: 512}
|
||||
default:
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
func (m *signingMethod) Verify(signingString, signature string, key interface{}) error {
|
||||
switch m.Name {
|
||||
case "HS256", "HS384", "HS512":
|
||||
{
|
||||
// Verify the key is the right type
|
||||
keyBytes, ok := key.([]byte)
|
||||
if !ok {
|
||||
return errInvalidKeyType
|
||||
}
|
||||
|
||||
// Decode signature, for comparison
|
||||
sig, err := decodeSegment(signature)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Can we use the specified hashing method?
|
||||
if !m.Hash.Available() {
|
||||
return errHashUnavailable
|
||||
}
|
||||
|
||||
// This signing method is symmetric, so we validate the signature
|
||||
// by reproducing the signature from the signing string and key, then
|
||||
// comparing that against the provided signature.
|
||||
hasher := hmac.New(m.Hash.New, keyBytes)
|
||||
hasher.Write([]byte(signingString))
|
||||
if !hmac.Equal(sig, hasher.Sum(nil)) {
|
||||
return errSignatureInvalid
|
||||
}
|
||||
|
||||
// No validation errors. Signature is good.
|
||||
return nil
|
||||
}
|
||||
case "RS256", "RS384", "RS512":
|
||||
{
|
||||
var err error
|
||||
|
||||
// Decode the signature
|
||||
var sig []byte
|
||||
if sig, err = decodeSegment(signature); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
var rsaKey *rsa.PublicKey
|
||||
var ok bool
|
||||
|
||||
if rsaKey, ok = key.(*rsa.PublicKey); !ok {
|
||||
return errInvalidKeyType
|
||||
}
|
||||
|
||||
// Create hasher
|
||||
if !m.Hash.Available() {
|
||||
return errHashUnavailable
|
||||
}
|
||||
hasher := m.Hash.New()
|
||||
hasher.Write([]byte(signingString))
|
||||
|
||||
// Verify the signature
|
||||
return rsa.VerifyPKCS1v15(rsaKey, m.Hash, hasher.Sum(nil), sig)
|
||||
}
|
||||
case "ES256", "ES384", "ES512":
|
||||
{
|
||||
var err error
|
||||
|
||||
// Decode the signature
|
||||
var sig []byte
|
||||
if sig, err = decodeSegment(signature); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// GetEmployee the key
|
||||
var ecdsaKey *ecdsa.PublicKey
|
||||
switch k := key.(type) {
|
||||
case *ecdsa.PublicKey:
|
||||
ecdsaKey = k
|
||||
default:
|
||||
return errInvalidKeyType
|
||||
}
|
||||
|
||||
if len(sig) != 2*m.KeySize {
|
||||
return errECDSAVerification
|
||||
}
|
||||
|
||||
r := big.NewInt(0).SetBytes(sig[:m.KeySize])
|
||||
s := big.NewInt(0).SetBytes(sig[m.KeySize:])
|
||||
|
||||
// Create hasher
|
||||
if !m.Hash.Available() {
|
||||
return errHashUnavailable
|
||||
}
|
||||
hasher := m.Hash.New()
|
||||
hasher.Write([]byte(signingString))
|
||||
|
||||
// Verify the signature
|
||||
if verifystatus := ecdsa.Verify(ecdsaKey, hasher.Sum(nil), r, s); verifystatus == true {
|
||||
return nil
|
||||
}
|
||||
|
||||
return errECDSAVerification
|
||||
}
|
||||
default:
|
||||
return errInvalidSigningMethod
|
||||
}
|
||||
}
|
||||
|
||||
func (m *signingMethod) Sign(signingString string, key interface{}) (string, error) {
|
||||
switch m.Name {
|
||||
case "HS256", "HS384", "HS512":
|
||||
{
|
||||
if keyBytes, ok := key.([]byte); ok {
|
||||
if !m.Hash.Available() {
|
||||
return "", errHashUnavailable
|
||||
}
|
||||
|
||||
hasher := hmac.New(m.Hash.New, keyBytes)
|
||||
hasher.Write([]byte(signingString))
|
||||
|
||||
return encodeSegment(hasher.Sum(nil)), nil
|
||||
}
|
||||
|
||||
return "", errInvalidKeyType
|
||||
}
|
||||
case "RS256", "RS384", "RS512":
|
||||
{
|
||||
var rsaKey *rsa.PrivateKey
|
||||
var ok bool
|
||||
|
||||
// Validate type of key
|
||||
if rsaKey, ok = key.(*rsa.PrivateKey); !ok {
|
||||
return "", errInvalidKey
|
||||
}
|
||||
|
||||
// Create the hasher
|
||||
if !m.Hash.Available() {
|
||||
return "", errHashUnavailable
|
||||
}
|
||||
|
||||
hasher := m.Hash.New()
|
||||
hasher.Write([]byte(signingString))
|
||||
|
||||
// Sign the string and return the encoded bytes
|
||||
if sigBytes, err := rsa.SignPKCS1v15(rand.Reader, rsaKey, m.Hash, hasher.Sum(nil)); err == nil {
|
||||
return encodeSegment(sigBytes), nil
|
||||
} else {
|
||||
return "", err
|
||||
}
|
||||
}
|
||||
case "ES256", "ES384", "ES512":
|
||||
{
|
||||
// GetEmployee the key
|
||||
var ecdsaKey *ecdsa.PrivateKey
|
||||
switch k := key.(type) {
|
||||
case *ecdsa.PrivateKey:
|
||||
ecdsaKey = k
|
||||
default:
|
||||
return "", errInvalidKeyType
|
||||
}
|
||||
|
||||
// Create the hasher
|
||||
if !m.Hash.Available() {
|
||||
return "", errHashUnavailable
|
||||
}
|
||||
|
||||
hasher := m.Hash.New()
|
||||
hasher.Write([]byte(signingString))
|
||||
|
||||
// Sign the string and return r, s
|
||||
if r, s, err := ecdsa.Sign(rand.Reader, ecdsaKey, hasher.Sum(nil)); err == nil {
|
||||
curveBits := ecdsaKey.Curve.Params().BitSize
|
||||
|
||||
if m.CurveBits != curveBits {
|
||||
return "", errInvalidKey
|
||||
}
|
||||
|
||||
keyBytes := curveBits / 8
|
||||
if curveBits%8 > 0 {
|
||||
keyBytes++
|
||||
}
|
||||
|
||||
// We serialize the outpus (r and s) into big-endian byte arrays and pad
|
||||
// them with zeros on the left to make sure the sizes work out. Both arrays
|
||||
// must be keyBytes long, and the output must be 2*keyBytes long.
|
||||
rBytes := r.Bytes()
|
||||
rBytesPadded := make([]byte, keyBytes)
|
||||
copy(rBytesPadded[keyBytes-len(rBytes):], rBytes)
|
||||
|
||||
sBytes := s.Bytes()
|
||||
sBytesPadded := make([]byte, keyBytes)
|
||||
copy(sBytesPadded[keyBytes-len(sBytes):], sBytes)
|
||||
|
||||
out := append(rBytesPadded, sBytesPadded...)
|
||||
|
||||
return encodeSegment(out), nil
|
||||
} else {
|
||||
return "", err
|
||||
}
|
||||
}
|
||||
default:
|
||||
{
|
||||
return "", errInvalidSigningMethod
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func methodEnable(method string) bool {
|
||||
if method == "HS256" || method == "HS384" || method == "HS512" || method == "RS256" || method == "RS384" || method == "RS512" || method == "ES256" || method == "ES384" || method == "ES512" {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// Decode JWT specific base64url encoding with padding stripped
|
||||
func decodeSegment(seg string) ([]byte, error) {
|
||||
if l := len(seg) % 4; l > 0 {
|
||||
seg += strings.Repeat("=", 4-l)
|
||||
}
|
||||
|
||||
return base64.URLEncoding.DecodeString(seg)
|
||||
}
|
||||
|
||||
// encode JWT specific base64url encoding with padding stripped
|
||||
func encodeSegment(seg []byte) string {
|
||||
return strings.TrimRight(base64.URLEncoding.EncodeToString(seg), "=")
|
||||
}
|
||||
|
||||
//ParseRSAPublicKeyFromPEM parse PEM encoded PKCS1 or PKCS8 public key
|
||||
func ParseRSAPublicKeyFromPEM(key []byte) (*rsa.PublicKey, error) {
|
||||
var err error
|
||||
|
||||
// parse PEM block
|
||||
var block *pem.Block
|
||||
if block, _ = pem.Decode(key); block == nil {
|
||||
return nil, errKeyMustBePEMEncoded
|
||||
}
|
||||
|
||||
// parse the key
|
||||
var parsedKey interface{}
|
||||
if parsedKey, err = x509.ParsePKIXPublicKey(block.Bytes); err != nil {
|
||||
if cert, err := x509.ParseCertificate(block.Bytes); err == nil {
|
||||
parsedKey = cert.PublicKey
|
||||
} else {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
var pkey *rsa.PublicKey
|
||||
var ok bool
|
||||
if pkey, ok = parsedKey.(*rsa.PublicKey); !ok {
|
||||
return nil, errNotRSAPublicKey
|
||||
}
|
||||
|
||||
return pkey, nil
|
||||
}
|
||||
|
||||
//ParseECPublicKeyFromPEM parse PEM encoded PKCS1 or PKCS8 public key
|
||||
func ParseECPublicKeyFromPEM(key []byte) (*ecdsa.PublicKey, error) {
|
||||
var err error
|
||||
|
||||
// parse PEM block
|
||||
var block *pem.Block
|
||||
if block, _ = pem.Decode(key); block == nil {
|
||||
return nil, errKeyMustBePEMEncoded
|
||||
}
|
||||
|
||||
// parse the key
|
||||
var parsedKey interface{}
|
||||
if parsedKey, err = x509.ParsePKIXPublicKey(block.Bytes); err != nil {
|
||||
if cert, err := x509.ParseCertificate(block.Bytes); err == nil {
|
||||
parsedKey = cert.PublicKey
|
||||
} else {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
var pkey *ecdsa.PublicKey
|
||||
var ok bool
|
||||
if pkey, ok = parsedKey.(*ecdsa.PublicKey); !ok {
|
||||
return nil, errNotECPublicKey
|
||||
}
|
||||
|
||||
return pkey, nil
|
||||
}
|
||||
|
||||
// base64解密
|
||||
func b64Decode(input string) (string, error) {
|
||||
remainder := len(input) % 4
|
||||
// base64编码需要为4的倍数,如果不是4的倍数,则填充"="号
|
||||
if remainder > 0 {
|
||||
padlen := 4 - remainder
|
||||
input = input + strings.Repeat("=", padlen)
|
||||
}
|
||||
// 将原字符串中的"_","-"分别用"/"和"+"替换
|
||||
input = strings.Replace(strings.Replace(input, "_", "/", -1), "-", "+", -1)
|
||||
result, err := base64.StdEncoding.DecodeString(input)
|
||||
return string(result), err
|
||||
}
|
||||
|
||||
// 根据"."分割token字符串
|
||||
func tokenize(token string) []string {
|
||||
parts := strings.Split(token, ".")
|
||||
if len(parts) == 3 {
|
||||
return parts
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// 解析token,将token信息解析为jwtToken对象
|
||||
func decodeToken(token string) (*jwtToken, error) {
|
||||
tokenParts := tokenize(token)
|
||||
if tokenParts == nil {
|
||||
return nil, errors.New("[jwt_auth] Invalid token")
|
||||
}
|
||||
header64 := tokenParts[0]
|
||||
claims64 := tokenParts[1]
|
||||
signature64 := tokenParts[2]
|
||||
var header, claims map[string]interface{}
|
||||
var signature string
|
||||
headerD64, err := b64Decode(header64)
|
||||
if err != nil {
|
||||
return nil, errors.New("[jwt_auth] Invalid base64 encoded JSON")
|
||||
}
|
||||
|
||||
if err = json.Unmarshal([]byte(headerD64), &header); err != nil {
|
||||
return nil, errors.New("[jwt_auth] Invalid JSON")
|
||||
}
|
||||
claimsD64, err := b64Decode(claims64)
|
||||
if err != nil {
|
||||
return nil, errors.New("[jwt_auth] Invalid base64 encoded JSON")
|
||||
}
|
||||
if err = json.Unmarshal([]byte(claimsD64), &claims); err != nil {
|
||||
return nil, errors.New("[jwt_auth] Invalid JSON")
|
||||
}
|
||||
signature, err = b64Decode(signature64)
|
||||
if err != nil {
|
||||
return nil, errors.New("[jwt_auth] Invalid base64 encoded JSON")
|
||||
}
|
||||
if _, ok := header["typ"]; !ok || strings.ToUpper(header["typ"].(string)) != "JWT" {
|
||||
return nil, errors.New("[jwt_auth] Invalid typ")
|
||||
}
|
||||
if _, ok := header["alg"]; !ok || !methodEnable(header["alg"].(string)) {
|
||||
return nil, errors.New("[jwt_auth] Invalid alg")
|
||||
}
|
||||
if len(claims) == 0 {
|
||||
return nil, errors.New("[jwt_auth] Invalid claims")
|
||||
}
|
||||
if len(signature) == 0 {
|
||||
return nil, errors.New("[jwt_auth] Invalid signature")
|
||||
}
|
||||
return &jwtToken{Token: token, Header64: header64, Claims64: claims64, Signature64: signature64, Header: header, Claims: claims, Signature: signature}, nil
|
||||
}
|
||||
|
||||
//verifySignature 验证签名
|
||||
func verifySignature(token *jwtToken, key string) error {
|
||||
|
||||
var k interface{}
|
||||
switch token.Header["alg"].(string) {
|
||||
case "HS256", "HS384", "HS512":
|
||||
{
|
||||
k = []byte(key)
|
||||
}
|
||||
case "RS256", "RS384", "RS512":
|
||||
{
|
||||
var err error
|
||||
k, err = ParseRSAPublicKeyFromPEM([]byte(key))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
case "ES256", "ES384", "ES512":
|
||||
{
|
||||
var err error
|
||||
k, err = ParseECPublicKeyFromPEM([]byte(key))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
default:
|
||||
{
|
||||
return errInvalidSigningMethod
|
||||
}
|
||||
}
|
||||
return newSigningMethod(token.Header["alg"].(string)).Verify(token.Header64+"."+token.Claims64, token.Signature64, k)
|
||||
}
|
||||
|
||||
//verifyRegisteredClaims 验证签发字段
|
||||
func verifyRegisteredClaims(token *jwtToken, claimsToVerify []string) error {
|
||||
if claimsToVerify == nil {
|
||||
claimsToVerify = []string{}
|
||||
}
|
||||
|
||||
for _, claimName := range claimsToVerify {
|
||||
var claim int64 = 0
|
||||
if _, ok := token.Claims[claimName]; ok {
|
||||
|
||||
if typeOfData(token.Claims[claimName]) == reflect.Float64 {
|
||||
claimFloat64, success := token.Claims[claimName].(float64)
|
||||
if success {
|
||||
claim = int64(claimFloat64)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
if claim < 1 {
|
||||
return errors.New("[jwt_auth] " + claimName + " must be a number")
|
||||
}
|
||||
switch claimName {
|
||||
case "nbf":
|
||||
if claim > time.Now().Unix() {
|
||||
return errors.New("[jwt_auth] token not valid yet")
|
||||
}
|
||||
case "exp":
|
||||
if claim <= time.Now().Unix() {
|
||||
return errors.New("[jwt_auth] token expired")
|
||||
}
|
||||
default:
|
||||
return errors.New("[jwt_auth] Invalid claims")
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
//获取数据的类型
|
||||
func typeOfData(data interface{}) reflect.Kind {
|
||||
value := reflect.ValueOf(data)
|
||||
valueType := value.Kind()
|
||||
if valueType == reflect.Ptr {
|
||||
valueType = value.Elem().Kind()
|
||||
}
|
||||
return valueType
|
||||
}
|
||||
|
||||
//retrieveJWTToken 获取jwtToken字符串
|
||||
func (j *jwt) retrieveJWTToken(context http_service.IHttpContext) (string, error) {
|
||||
const tokenName = "jwt_token"
|
||||
if authorizationHeader := context.Request().Header().GetHeader("Authorization"); authorizationHeader != "" {
|
||||
if j.hideCredentials {
|
||||
context.Proxy().Header().DelHeader("Authorization")
|
||||
}
|
||||
if strings.Contains(authorizationHeader, "bearer ") {
|
||||
authorizationHeader = authorizationHeader[7:]
|
||||
}
|
||||
return authorizationHeader, nil
|
||||
}
|
||||
|
||||
if value := context.Proxy().URI().GetQuery(tokenName); value != "" {
|
||||
if j.hideCredentials {
|
||||
context.Proxy().URI().DelQuery(tokenName)
|
||||
}
|
||||
return value, nil
|
||||
}
|
||||
|
||||
formData, err := context.Proxy().Body().BodyForm()
|
||||
if err != nil {
|
||||
return "", errors.New("[jwt_auth] cannot find token in request")
|
||||
}
|
||||
if value, ok := formData[tokenName]; ok {
|
||||
if j.hideCredentials {
|
||||
delete(formData, tokenName)
|
||||
context.Proxy().Body().SetForm(formData)
|
||||
}
|
||||
return value[0], nil
|
||||
}
|
||||
return "", errors.New("[jwt_auth] cannot find token in request")
|
||||
}
|
||||
|
||||
//doJWTAuthentication 进行JWT鉴权
|
||||
func (j *jwt) doJWTAuthentication(context http_service.IHttpContext) error {
|
||||
tokenStr, err := j.retrieveJWTToken(context)
|
||||
if err != nil {
|
||||
return errors.New("[jwt_auth] Unrecognizable token")
|
||||
}
|
||||
token, err := decodeToken(tokenStr)
|
||||
if err != nil {
|
||||
return errors.New("[jwt_auth] Bad token; " + err.Error())
|
||||
}
|
||||
|
||||
key := ""
|
||||
keyClaimName := "iss"
|
||||
if _, ok := token.Claims[keyClaimName]; ok {
|
||||
key = token.Claims[keyClaimName].(string)
|
||||
} else if _, ok = token.Header[keyClaimName]; ok {
|
||||
key = token.Header[keyClaimName].(string)
|
||||
}
|
||||
|
||||
if key == "" {
|
||||
return errors.New("[jwt_auth] No mandatory " + keyClaimName + " in claims")
|
||||
}
|
||||
|
||||
// 从配置中获取jwt凭证配置
|
||||
|
||||
jwtSecret, err := loadCredential(context, j.credentials, key, token.Header["alg"].(string))
|
||||
if err != nil {
|
||||
return errors.New("[jwt_auth] No credentials found for given " + keyClaimName)
|
||||
}
|
||||
|
||||
jwtSecretValue := jwtSecret.RSAPublicKey
|
||||
algorithm := "HS256"
|
||||
if jwtSecret.Algorithm != "" {
|
||||
algorithm = jwtSecret.Algorithm
|
||||
}
|
||||
if algorithm == "HS256" || algorithm == "HS384" || algorithm == "HS512" {
|
||||
jwtSecretValue = jwtSecret.Secret
|
||||
}
|
||||
if j.signatureIsBase64 {
|
||||
jwtSecretValue, err = b64Decode(jwtSecretValue)
|
||||
if err != nil {
|
||||
return errors.New("[jwt_auth] Invalid key/secret")
|
||||
}
|
||||
}
|
||||
if jwtSecretValue == "" {
|
||||
return errors.New("[jwt_auth] Invalid key/secret")
|
||||
}
|
||||
|
||||
if err = verifySignature(token, jwtSecretValue); err != nil {
|
||||
return errors.New("[jwt_auth] Invalid signature")
|
||||
}
|
||||
if err = verifyRegisteredClaims(token, j.claimsToVerify); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// 从配置中获取jwt凭证配置
|
||||
func loadCredential(context http_service.IHttpContext, conf *jwtUsers, key, alg string) (JwtCredential, error) {
|
||||
|
||||
for _, credential := range conf.credentials {
|
||||
if credential.Iss == key {
|
||||
if credential.Algorithm == alg {
|
||||
//将label set进context
|
||||
for k, v := range credential.Labels {
|
||||
context.SetLabel(k, v)
|
||||
}
|
||||
|
||||
return credential, nil
|
||||
}
|
||||
}
|
||||
}
|
||||
return JwtCredential{}, errors.New("[jwt_auth] Invalid jwt secret key")
|
||||
}
|
||||
@@ -1,11 +0,0 @@
|
||||
package auth
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/eolinker/eosc"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestAuth(t *testing.T) {
|
||||
log.Debug(config.TypeNameOf((*IAuth)(nil)))
|
||||
}
|
||||
35
drivers/plugins/app/app.go
Normal file
35
drivers/plugins/app/app.go
Normal file
@@ -0,0 +1,35 @@
|
||||
package app
|
||||
|
||||
import (
|
||||
"github.com/eolinker/apinto/auth"
|
||||
"github.com/eolinker/eosc"
|
||||
)
|
||||
|
||||
type App struct {
|
||||
auths []auth.IAuth
|
||||
}
|
||||
|
||||
func (a *App) Id() string {
|
||||
//TODO implement me
|
||||
panic("implement me")
|
||||
}
|
||||
|
||||
func (a *App) Start() error {
|
||||
//TODO implement me
|
||||
panic("implement me")
|
||||
}
|
||||
|
||||
func (a *App) Reset(conf interface{}, workers map[eosc.RequireId]eosc.IWorker) error {
|
||||
//TODO implement me
|
||||
panic("implement me")
|
||||
}
|
||||
|
||||
func (a *App) Stop() error {
|
||||
//TODO implement me
|
||||
panic("implement me")
|
||||
}
|
||||
|
||||
func (a *App) CheckSkill(skill string) bool {
|
||||
//TODO implement me
|
||||
panic("implement me")
|
||||
}
|
||||
52
drivers/plugins/app/config.go
Normal file
52
drivers/plugins/app/config.go
Normal file
@@ -0,0 +1,52 @@
|
||||
package app
|
||||
|
||||
import (
|
||||
"github.com/eolinker/eosc"
|
||||
"github.com/eolinker/eosc/common/bean"
|
||||
"sync"
|
||||
)
|
||||
|
||||
var (
|
||||
extenders eosc.IExtenderDrivers
|
||||
ones sync.Once
|
||||
)
|
||||
|
||||
type Config struct {
|
||||
Auth []*AuthConfig `json:"auth" label:"鉴权列表"`
|
||||
}
|
||||
|
||||
type AuthConfig struct {
|
||||
Config interface{} `json:"config"`
|
||||
DriverID string `json:"driver_id"`
|
||||
Position string `json:"position"`
|
||||
TokenName string `json:"token_name"`
|
||||
Users []*User `json:"users"`
|
||||
}
|
||||
|
||||
type User struct {
|
||||
Expire int `json:"expire"`
|
||||
Labels map[string]string `json:"labels"`
|
||||
Pattern string `json:"pattern"`
|
||||
HideCredential bool `json:"hide_credential"`
|
||||
}
|
||||
|
||||
func Check(v interface{}) (*Config, error) {
|
||||
ones.Do(func() {
|
||||
bean.Autowired(&extenders)
|
||||
})
|
||||
cfg, ok := v.(*Config)
|
||||
if !ok {
|
||||
return nil, eosc.ErrorConfigFieldUnknown
|
||||
}
|
||||
for _, a := range cfg.Auth {
|
||||
fac, has := extenders.GetDriver(a.DriverID)
|
||||
if !has {
|
||||
return nil, eosc.ErrorDriverNotExist
|
||||
}
|
||||
driver, err := fac.Create("auth", "", "", "", nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
return nil, nil
|
||||
}
|
||||
63
drivers/plugins/app/driver.go
Normal file
63
drivers/plugins/app/driver.go
Normal file
@@ -0,0 +1,63 @@
|
||||
package app
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/eolinker/eosc/log"
|
||||
"reflect"
|
||||
|
||||
"github.com/eolinker/apinto/auth"
|
||||
"github.com/eolinker/eosc"
|
||||
)
|
||||
|
||||
type Driver struct {
|
||||
profession string
|
||||
name string
|
||||
label string
|
||||
desc string
|
||||
workers eosc.IWorkers
|
||||
configType reflect.Type
|
||||
}
|
||||
|
||||
func (d *Driver) Check(v interface{}, workers map[eosc.RequireId]eosc.IWorker) error {
|
||||
_, err := d.check(v)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (d *Driver) check(v interface{}) (*Config, error) {
|
||||
conf, ok := v.(*Config)
|
||||
if !ok {
|
||||
return nil, eosc.ErrorConfigFieldUnknown
|
||||
}
|
||||
return conf, nil
|
||||
}
|
||||
|
||||
func (d *Driver) ConfigType() reflect.Type {
|
||||
return d.configType
|
||||
}
|
||||
|
||||
func (d *Driver) getList(auths []eosc.RequireId) ([]auth.IAuth, error) {
|
||||
ls := make([]auth.IAuth, 0, len(auths))
|
||||
for _, id := range auths {
|
||||
worker, has := d.workers.Get(string(id))
|
||||
if !has {
|
||||
return nil, fmt.Errorf("%s:%w", id, eosc.ErrorWorkerNotExits)
|
||||
}
|
||||
if !worker.CheckSkill(auth.AuthSkill) {
|
||||
return nil, fmt.Errorf("%s:%w:%s", id, eosc.ErrorTargetNotImplementSkill, auth.AuthSkill)
|
||||
}
|
||||
ls = append(ls, worker.(auth.IAuth))
|
||||
|
||||
}
|
||||
return ls, nil
|
||||
}
|
||||
func (d *Driver) Create(id, name string, v interface{}, workers map[eosc.RequireId]eosc.IWorker) (eosc.IWorker, error) {
|
||||
conf, err := d.check(v)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
log.Info(conf)
|
||||
return nil, nil
|
||||
}
|
||||
42
drivers/plugins/app/factory.go
Normal file
42
drivers/plugins/app/factory.go
Normal file
@@ -0,0 +1,42 @@
|
||||
package app
|
||||
|
||||
import (
|
||||
"github.com/eolinker/eosc"
|
||||
"github.com/eolinker/eosc/common/bean"
|
||||
"github.com/eolinker/eosc/utils/schema"
|
||||
"reflect"
|
||||
)
|
||||
|
||||
const (
|
||||
Name = "app"
|
||||
)
|
||||
|
||||
func Register(register eosc.IExtenderDriverRegister) {
|
||||
register.RegisterExtenderDriver(Name, NewFactory())
|
||||
}
|
||||
|
||||
type Factory struct {
|
||||
}
|
||||
|
||||
func (f *Factory) Render() interface{} {
|
||||
render, err := schema.Generate(reflect.TypeOf((*Config)(nil)), nil)
|
||||
if err != nil {
|
||||
return nil
|
||||
}
|
||||
return render
|
||||
}
|
||||
func NewFactory() *Factory {
|
||||
return &Factory{}
|
||||
}
|
||||
|
||||
func (f *Factory) Create(profession string, name string, label string, desc string, params map[string]interface{}) (eosc.IExtenderDriver, error) {
|
||||
d := &Driver{
|
||||
profession: profession,
|
||||
name: name,
|
||||
label: label,
|
||||
desc: desc,
|
||||
configType: reflect.TypeOf((*Config)(nil)),
|
||||
}
|
||||
bean.Autowired(&d.workers)
|
||||
return d, nil
|
||||
}
|
||||
7
drivers/plugins/app/manager.go
Normal file
7
drivers/plugins/app/manager.go
Normal file
@@ -0,0 +1,7 @@
|
||||
package app
|
||||
|
||||
import "github.com/eolinker/eosc"
|
||||
|
||||
type Manager struct {
|
||||
AuthFactory map[string]eosc.IExtenderDriverFactory
|
||||
}
|
||||
@@ -1,6 +1,7 @@
|
||||
package params_transformer
|
||||
|
||||
import (
|
||||
"github.com/eolinker/eosc/log"
|
||||
"github.com/eolinker/eosc/utils/schema"
|
||||
"reflect"
|
||||
|
||||
@@ -12,6 +13,7 @@ const (
|
||||
)
|
||||
|
||||
func Register(register eosc.IExtenderDriverRegister) {
|
||||
log.Debug("register params_transformer is ", Name)
|
||||
register.RegisterExtenderDriver(Name, NewFactory())
|
||||
}
|
||||
|
||||
|
||||
@@ -2,6 +2,7 @@ package plugin_manager
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/eolinker/eosc/log"
|
||||
"github.com/eolinker/eosc/variable"
|
||||
"reflect"
|
||||
)
|
||||
@@ -48,7 +49,7 @@ func (p *PluginConfig) GetType(originVal reflect.Value) (reflect.Type, error) {
|
||||
params = tmp
|
||||
}
|
||||
}
|
||||
|
||||
log.Debug("plugin reset id is: ", id)
|
||||
factory, has := singleton.extenderDrivers.GetDriver(id)
|
||||
if !has {
|
||||
return nil, fmt.Errorf("driver(%s) not found", id)
|
||||
|
||||
Reference in New Issue
Block a user