应用初次提交

This commit is contained in:
Liujian
2022-08-19 21:20:09 +08:00
parent f4cce0aac9
commit 0e6e55dc82
32 changed files with 2786 additions and 12 deletions

View File

@@ -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 {

View 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")
}

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

View 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:"过期时间"`
}

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

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

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

View 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)
}

View 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")
}

View 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:"过期时间"`
}

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

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

View 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")
}

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

View 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:"过期时间"`
}

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

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

View 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:"用户标签"`
}

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

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

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

View 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)
}
})
}
}

View 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")
}

View File

@@ -1,11 +0,0 @@
package auth
import (
"fmt"
"github.com/eolinker/eosc"
"testing"
)
func TestAuth(t *testing.T) {
log.Debug(config.TypeNameOf((*IAuth)(nil)))
}

View 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")
}

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

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

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

View File

@@ -0,0 +1,7 @@
package app
import "github.com/eolinker/eosc"
type Manager struct {
AuthFactory map[string]eosc.IExtenderDriverFactory
}

View File

@@ -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())
}

View File

@@ -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)