Create & Init Project...

This commit is contained in:
2019-04-22 18:49:16 +08:00
commit fc4fa37393
25440 changed files with 4054998 additions and 0 deletions

View File

@@ -0,0 +1,64 @@
package(default_visibility = ["//visibility:public"])
load(
"@io_bazel_rules_go//go:def.bzl",
"go_test",
"go_library",
)
go_test(
name = "go_default_test",
srcs = ["verify_test.go"],
embed = [":go_default_library"],
rundir = ".",
tags = ["automanaged"],
deps = [
"//library/log:go_default_library",
"//library/net/http/blademaster:go_default_library",
"//library/net/metadata:go_default_library",
"//library/net/netutil/breaker:go_default_library",
"//library/time:go_default_library",
"//vendor/github.com/stretchr/testify/assert:go_default_library",
],
)
go_library(
name = "go_default_library",
srcs = ["verify.go"],
importpath = "go-common/library/net/http/blademaster/middleware/verify",
tags = ["automanaged"],
visibility = ["//visibility:public"],
deps = [
"//library/ecode:go_default_library",
"//library/log:go_default_library",
"//library/net/http/blademaster:go_default_library",
"//library/net/metadata:go_default_library",
"//library/time:go_default_library",
],
)
go_test(
name = "go_default_xtest",
srcs = ["example_test.go"],
tags = ["automanaged"],
deps = [
"//library/net/http/blademaster:go_default_library",
"//library/net/http/blademaster/middleware/verify:go_default_library",
"//library/net/metadata:go_default_library",
"//library/time:go_default_library",
],
)
filegroup(
name = "package-srcs",
srcs = glob(["**"]),
tags = ["automanaged"],
visibility = ["//visibility:private"],
)
filegroup(
name = "all-srcs",
srcs = [":package-srcs"],
tags = ["automanaged"],
visibility = ["//visibility:public"],
)

View File

@@ -0,0 +1,5 @@
#### library/net/http/blademaster/middleware/verify
##### Version 1.0.0
1. 独立的 bm verify 中间件

View File

@@ -0,0 +1,10 @@
# Owner
maojian
zhoujiahui
# Author
zhoujiahui
# Reviewer
maojian
haoguanwei

View File

@@ -0,0 +1,9 @@
# See the OWNERS docs at https://go.k8s.io/owners
approvers:
- maojian
- zhoujiahui
reviewers:
- haoguanwei
- maojian
- zhoujiahui

View File

@@ -0,0 +1,13 @@
#### library/net/http/blademaster/middleware/verify
##### 项目简介
blademaster 的 verify middleware主要用于设置路由的校验策略
##### 编译环境
- **请只用 Golang v1.10.x 以上版本编译执行**
##### 依赖包
- No other dependency

View File

@@ -0,0 +1,41 @@
package verify_test
import (
"fmt"
"time"
bm "go-common/library/net/http/blademaster"
"go-common/library/net/http/blademaster/middleware/verify"
"go-common/library/net/metadata"
xtime "go-common/library/time"
)
// This example create a identify middleware instance and attach to several path,
// it will validate request by specified policy and put extra information into context. e.g., `mid`.
// It provides additional handler functions to provide the identification for your business handler.
func Example() {
idt := verify.New(&verify.Config{
OpenServiceHost: "http://uat-open.bilibili.co",
HTTPClient: &bm.ClientConfig{
App: &bm.App{
Key: "53e2fa226f5ad348",
Secret: "3cf6bd1b0ff671021da5f424fea4b04a",
},
Dial: xtime.Duration(time.Second),
Timeout: xtime.Duration(time.Second),
KeepAlive: xtime.Duration(time.Second * 10),
},
})
e := bm.Default()
// mark `/verify` path as Verify policy
e.GET("/verify", idt.Verify, func(c *bm.Context) {
c.JSON("pass", nil)
})
// mark `/verify` path as VerifyUser policy
e.GET("/verifyUser", idt.VerifyUser, func(c *bm.Context) {
mid := metadata.Int64(c, metadata.Mid)
c.JSON(fmt.Sprintf("%d", mid), nil)
})
e.Run(":18080")
}

View File

@@ -0,0 +1,172 @@
package verify
import (
"crypto/md5"
"encoding/hex"
"net/url"
"strings"
"sync"
"time"
"go-common/library/ecode"
"go-common/library/log"
bm "go-common/library/net/http/blademaster"
"go-common/library/net/metadata"
xtime "go-common/library/time"
)
const (
_secretURI = "/api/getsecret"
)
// Verify is is the verify model.
type Verify struct {
lock sync.RWMutex
keys map[string]string
secretURI string
client *bm.Client
}
// Config is the verify config model.
type Config struct {
OpenServiceHost string
HTTPClient *bm.ClientConfig
}
var _defaultConfig = &Config{
OpenServiceHost: "http://open.bilibili.co",
HTTPClient: &bm.ClientConfig{
App: &bm.App{
Key: "53e2fa226f5ad348",
Secret: "3cf6bd1b0ff671021da5f424fea4b04a",
},
Dial: xtime.Duration(time.Millisecond * 100),
Timeout: xtime.Duration(time.Millisecond * 300),
KeepAlive: xtime.Duration(time.Second * 60),
},
}
// New will create a verify middleware by given config.
// panic on conf is nil.
func New(conf *Config) *Verify {
if conf == nil {
conf = _defaultConfig
}
v := &Verify{
keys: make(map[string]string),
client: bm.NewClient(conf.HTTPClient),
secretURI: conf.OpenServiceHost + _secretURI,
}
return v
}
func (v *Verify) verify(ctx *bm.Context) error {
req := ctx.Request
params := req.Form
if req.Method == "POST" {
// Give priority to sign in url query, otherwise check sign in post form.
q := req.URL.Query()
if q.Get("sign") != "" {
params = q
}
}
// check timestamp is not empty (TODO : Check if out of some seconds.., like 100s)
if params.Get("ts") == "" {
log.Error("ts is empty")
return ecode.RequestErr
}
sign := params.Get("sign")
params.Del("sign")
defer params.Set("sign", sign)
sappkey := params.Get("appkey")
v.lock.RLock()
secret, ok := v.keys[sappkey]
v.lock.RUnlock()
if !ok {
fetched, err := v.fetchSecret(ctx, sappkey)
if err != nil {
return err
}
v.lock.Lock()
v.keys[sappkey] = fetched
v.lock.Unlock()
secret = fetched
}
if hsign := Sign(params, sappkey, secret, true); hsign != sign {
if hsign1 := Sign(params, sappkey, secret, false); hsign1 != sign {
log.Error("Get sign: %s, expect %s", sign, hsign)
return ecode.SignCheckErr
}
}
return nil
}
// Verify will inject into handler func as verify required
func (v *Verify) Verify(ctx *bm.Context) {
if err := v.verify(ctx); err != nil {
ctx.JSON(nil, err)
ctx.Abort()
return
}
}
// VerifyUser is used to mark path as verify and mid required.
func (v *Verify) VerifyUser(ctx *bm.Context) {
if err := v.verify(ctx); err != nil {
ctx.JSON(nil, err)
ctx.Abort()
return
}
var midReq struct {
Mid int64 `form:"mid" validate:"required"`
}
if err := ctx.Bind(&midReq); err != nil {
return
}
ctx.Set("mid", midReq.Mid)
if md, ok := metadata.FromContext(ctx); ok {
md[metadata.Mid] = midReq.Mid
}
}
// Sign is used to sign form params by given condition.
func Sign(params url.Values, appkey string, secret string, lower bool) string {
data := params.Encode()
if strings.IndexByte(data, '+') > -1 {
data = strings.Replace(data, "+", "%20", -1)
}
if lower {
data = strings.ToLower(data)
}
digest := md5.Sum([]byte(data + secret))
return hex.EncodeToString(digest[:])
}
func (v *Verify) fetchSecret(ctx *bm.Context, appkey string) (string, error) {
params := url.Values{}
var resp struct {
Code int `json:"code"`
Data struct {
AppSecret string `json:"app_secret"`
} `json:"data"`
}
params.Set("sappkey", appkey)
if err := v.client.Get(ctx, v.secretURI, metadata.String(ctx, metadata.RemoteIP), params, &resp); err != nil {
return "", err
}
if resp.Code != 0 || resp.Data.AppSecret == "" {
log.Error("Failed to fetch secret with request(%s, %s) code(%d)", v.secretURI, params.Encode(), resp.Code)
if resp.Code != 0 {
return "", ecode.Int(resp.Code)
}
return "", ecode.ServerErr
}
return resp.Data.AppSecret, nil
}

View File

@@ -0,0 +1,175 @@
package verify
import (
"context"
"encoding/json"
"fmt"
"io/ioutil"
"net/http"
"net/url"
"testing"
"time"
"go-common/library/log"
bm "go-common/library/net/http/blademaster"
"go-common/library/net/metadata"
"go-common/library/net/netutil/breaker"
xtime "go-common/library/time"
"github.com/stretchr/testify/assert"
)
type Response struct {
Code int `json:"code"`
Data string `json:"data"`
}
func init() {
log.Init(&log.Config{
Stdout: true,
})
}
func verify() *Verify {
return New(&Config{
OpenServiceHost: "http://uat-open.bilibili.co",
HTTPClient: &bm.ClientConfig{
App: &bm.App{
Key: "53e2fa226f5ad348",
Secret: "3cf6bd1b0ff671021da5f424fea4b04a",
},
Dial: xtime.Duration(time.Second),
Timeout: xtime.Duration(time.Second),
KeepAlive: xtime.Duration(time.Second * 10),
Breaker: &breaker.Config{
Window: xtime.Duration(time.Second),
Sleep: xtime.Duration(time.Millisecond * 100),
Bucket: 10,
Ratio: 0.5,
Request: 100,
},
},
})
}
func client() *bm.Client {
return bm.NewClient(&bm.ClientConfig{
App: &bm.App{
Key: "53e2fa226f5ad348",
Secret: "3cf6bd1b0ff671021da5f424fea4b04a",
},
Dial: xtime.Duration(time.Second),
Timeout: xtime.Duration(time.Second),
KeepAlive: xtime.Duration(time.Second * 10),
Breaker: &breaker.Config{
Window: xtime.Duration(time.Second),
Sleep: xtime.Duration(time.Millisecond * 100),
Bucket: 10,
Ratio: 0.5,
Request: 100,
},
})
}
func engine() *bm.Engine {
e := bm.New()
idt := verify()
e.GET("/verify", idt.Verify, func(c *bm.Context) {
c.JSON("pass", nil)
})
e.GET("/verifyUser", idt.VerifyUser, func(c *bm.Context) {
mid := metadata.Int64(c, metadata.Mid)
fmt.Println(mid)
c.JSON(fmt.Sprintf("%d", mid), nil)
})
return e
}
func TestNewWithNilConfig(t *testing.T) {
New(nil)
}
func TestVerifyIdentifyHandler(t *testing.T) {
e := engine()
go e.Run(":18080")
time.Sleep(time.Second)
// test cases
testVerifyFailed(t)
testVerifySuccess(t)
testVerifyUser(t)
testVerifyUserFailed(t)
testVerifyUserInvalid(t)
if err := e.Server().Shutdown(context.TODO()); err != nil {
t.Logf("Failed to shutdown bm engine: %v", err)
}
}
func httpGet(url string) (code int, content []byte, err error) {
resp, err := http.Get(url)
if err != nil {
return
}
defer resp.Body.Close()
content, err = ioutil.ReadAll(resp.Body)
if err != nil {
return
}
code = resp.StatusCode
return
}
func testVerifyFailed(t *testing.T) {
res := Response{}
code, content, err := httpGet("http://127.0.0.1:18080/verify?ts=1&appkey=53e2fa226f5ad348")
assert.NoError(t, err)
assert.Equal(t, 200, code)
err = json.Unmarshal(content, &res)
assert.NoError(t, err)
assert.Equal(t, -3, res.Code)
}
func testVerifySuccess(t *testing.T) {
res := Response{}
uv := url.Values{}
uv.Set("appkey", "53e2fa226f5ad348")
err := client().Get(context.TODO(), "http://127.0.0.1:18080/verify", "", uv, &res)
assert.NoError(t, err)
assert.Equal(t, 0, res.Code)
assert.Equal(t, "pass", res.Data)
}
func testVerifyUser(t *testing.T) {
res := Response{}
query := url.Values{}
query.Set("mid", "1")
query.Set("appkey", "53e2fa226f5ad348")
err := client().Get(context.TODO(), "http://127.0.0.1:18080/verifyUser", "", query, &res)
assert.NoError(t, err)
assert.Equal(t, 0, res.Code)
assert.Equal(t, "1", res.Data)
}
func testVerifyUserFailed(t *testing.T) {
res := Response{}
code, content, err := httpGet("http://127.0.0.1:18080/verifyUser?ts=1&appkey=53e2fa226f5ad348")
assert.NoError(t, err)
assert.Equal(t, 200, code)
err = json.Unmarshal(content, &res)
assert.NoError(t, err)
assert.Equal(t, -3, res.Code)
}
func testVerifyUserInvalid(t *testing.T) {
res := Response{}
query := url.Values{}
query.Set("mid", "aaaa/")
query.Set("appkey", "53e2fa226f5ad348")
err := client().Get(context.TODO(), "http://127.0.0.1:18080/verifyUser", "", query, &res)
assert.NoError(t, err)
assert.Equal(t, -400, res.Code)
assert.Equal(t, "", res.Data)
}