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,67 @@
package(default_visibility = ["//visibility:public"])
load(
"@io_bazel_rules_go//go:def.bzl",
"go_test",
"go_library",
)
go_test(
name = "go_default_test",
srcs = ["auth_test.go"],
embed = [":go_default_library"],
rundir = ".",
tags = ["automanaged"],
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/net/netutil/breaker:go_default_library",
"//library/net/rpc/warden:go_default_library",
"//library/time:go_default_library",
"//vendor/github.com/stretchr/testify/assert:go_default_library",
],
)
go_library(
name = "go_default_library",
srcs = ["auth.go"],
importpath = "go-common/library/net/http/blademaster/middleware/auth",
tags = ["automanaged"],
visibility = ["//visibility:public"],
deps = [
"//app/service/main/identify/api/grpc:go_default_library",
"//library/ecode:go_default_library",
"//library/net/http/blademaster:go_default_library",
"//library/net/metadata:go_default_library",
"//library/net/rpc/warden:go_default_library",
"//vendor/github.com/pkg/errors: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"],
)
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/auth:go_default_library",
"//library/net/metadata:go_default_library",
"//library/net/rpc/warden:go_default_library",
],
)

View File

@@ -0,0 +1,13 @@
#### library/net/http/blademaster/middleware/auth
##### Version 1.0.2
1. 将认证使用的方法作为公开方法
##### Version 1.0.1
1. 成功后将 mid 加入 metadata
##### Version 1.0.0
1. 完全使用 identify-service 来认证用户

View File

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

View File

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

View File

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

View File

@@ -0,0 +1,177 @@
package auth
import (
idtv1 "go-common/app/service/main/identify/api/grpc"
"go-common/library/ecode"
bm "go-common/library/net/http/blademaster"
"go-common/library/net/metadata"
"go-common/library/net/rpc/warden"
"github.com/pkg/errors"
)
// Config is the identify config model.
type Config struct {
Identify *warden.ClientConfig
// csrf switch.
DisableCSRF bool
}
// Auth is the authorization middleware
type Auth struct {
idtv1.IdentifyClient
conf *Config
}
// authFunc will return mid and error by given context
type authFunc func(*bm.Context) (int64, error)
var _defaultConf = &Config{
Identify: nil,
DisableCSRF: false,
}
// New is used to create an authorization middleware
func New(conf *Config) *Auth {
if conf == nil {
conf = _defaultConf
}
identify, err := idtv1.NewClient(conf.Identify)
if err != nil {
panic(errors.WithMessage(err, "Failed to dial identify service"))
}
auth := &Auth{
IdentifyClient: identify,
conf: conf,
}
return auth
}
// User is used to mark path as access required.
// If `access_key` is exist in request form, it will using mobile access policy.
// Otherwise to web access policy.
func (a *Auth) User(ctx *bm.Context) {
req := ctx.Request
if req.Form.Get("access_key") == "" {
a.UserWeb(ctx)
return
}
a.UserMobile(ctx)
}
// UserWeb is used to mark path as web access required.
func (a *Auth) UserWeb(ctx *bm.Context) {
a.midAuth(ctx, a.AuthCookie)
}
// UserMobile is used to mark path as mobile access required.
func (a *Auth) UserMobile(ctx *bm.Context) {
a.midAuth(ctx, a.AuthToken)
}
// Guest is used to mark path as guest policy.
// If `access_key` is exist in request form, it will using mobile access policy.
// Otherwise to web access policy.
func (a *Auth) Guest(ctx *bm.Context) {
req := ctx.Request
if req.Form.Get("access_key") == "" {
a.GuestWeb(ctx)
return
}
a.GuestMobile(ctx)
}
// GuestWeb is used to mark path as web guest policy.
func (a *Auth) GuestWeb(ctx *bm.Context) {
a.guestAuth(ctx, a.AuthCookie)
}
// GuestMobile is used to mark path as mobile guest policy.
func (a *Auth) GuestMobile(ctx *bm.Context) {
a.guestAuth(ctx, a.AuthToken)
}
// AuthToken is used to authorize request by token
func (a *Auth) AuthToken(ctx *bm.Context) (int64, error) {
req := ctx.Request
key := req.Form.Get("access_key")
if key == "" {
return 0, ecode.NoLogin
}
buvid := req.Header.Get("buvid")
reply, err := a.GetTokenInfo(ctx, &idtv1.GetTokenInfoReq{Token: key, Buvid: buvid})
if err != nil {
return 0, err
}
if !reply.IsLogin {
return 0, ecode.NoLogin
}
return reply.Mid, nil
}
// AuthCookie is used to authorize request by cookie
func (a *Auth) AuthCookie(ctx *bm.Context) (int64, error) {
req := ctx.Request
ssDaCk, _ := req.Cookie("SESSDATA")
if ssDaCk == nil {
return 0, ecode.NoLogin
}
cookie := req.Header.Get("Cookie")
reply, err := a.GetCookieInfo(ctx, &idtv1.GetCookieInfoReq{Cookie: cookie})
if err != nil {
return 0, err
}
if !reply.IsLogin {
return 0, ecode.NoLogin
}
// check csrf
clientCsrf := req.FormValue("csrf")
if a.conf != nil && !a.conf.DisableCSRF && req.Method == "POST" {
if clientCsrf != reply.Csrf {
return 0, ecode.CsrfNotMatchErr
}
}
return reply.Mid, nil
}
func (a *Auth) midAuth(ctx *bm.Context, auth authFunc) {
mid, err := auth(ctx)
if err != nil {
ctx.JSON(nil, err)
ctx.Abort()
return
}
setMid(ctx, mid)
}
func (a *Auth) guestAuth(ctx *bm.Context, auth authFunc) {
mid, err := auth(ctx)
// no error happened and mid is valid
if err == nil && mid > 0 {
setMid(ctx, mid)
return
}
ec := ecode.Cause(err)
if ec.Equal(ecode.CsrfNotMatchErr) {
ctx.JSON(nil, ec)
ctx.Abort()
return
}
}
// set mid into context
// NOTE: This method is not thread safe.
func setMid(ctx *bm.Context, mid int64) {
ctx.Set("mid", mid)
if md, ok := metadata.FromContext(ctx); ok {
md[metadata.Mid] = mid
return
}
}

View File

@@ -0,0 +1,341 @@
package auth
import (
"bytes"
"context"
"fmt"
"mime/multipart"
"net/http"
"net/url"
"testing"
"time"
"go-common/library/ecode"
"go-common/library/log"
bm "go-common/library/net/http/blademaster"
"go-common/library/net/metadata"
"go-common/library/net/netutil/breaker"
"go-common/library/net/rpc/warden"
xtime "go-common/library/time"
"github.com/stretchr/testify/assert"
)
const (
_testUID = "2231365"
)
type Response struct {
Code int `json:"code"`
Data string `json:"data"`
}
func init() {
log.Init(&log.Config{
Stdout: true,
})
}
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 create() *Auth {
return New(&Config{
Identify: &warden.ClientConfig{},
DisableCSRF: false,
})
}
func engine() *bm.Engine {
e := bm.NewServer(nil)
authn := create()
e.GET("/user", authn.User, func(ctx *bm.Context) {
mid, _ := ctx.Get("mid")
ctx.JSON(fmt.Sprintf("%d", mid), nil)
})
e.GET("/metadata/user", authn.User, func(ctx *bm.Context) {
mid := metadata.Value(ctx, metadata.Mid)
ctx.JSON(fmt.Sprintf("%d", mid.(int64)), nil)
})
e.GET("/mobile", authn.UserMobile, func(ctx *bm.Context) {
mid, _ := ctx.Get("mid")
ctx.JSON(fmt.Sprintf("%d", mid), nil)
})
e.GET("/metadata/mobile", authn.UserMobile, func(ctx *bm.Context) {
mid := metadata.Value(ctx, metadata.Mid)
ctx.JSON(fmt.Sprintf("%d", mid.(int64)), nil)
})
e.GET("/web", authn.UserWeb, func(ctx *bm.Context) {
mid, _ := ctx.Get("mid")
ctx.JSON(fmt.Sprintf("%d", mid), nil)
})
e.GET("/guest", authn.Guest, func(ctx *bm.Context) {
var (
mid int64
)
if _mid, ok := ctx.Get("mid"); ok {
mid, _ = _mid.(int64)
}
ctx.JSON(fmt.Sprintf("%d", mid), nil)
})
e.GET("/guest/web", authn.GuestWeb, func(ctx *bm.Context) {
var (
mid int64
)
if _mid, ok := ctx.Get("mid"); ok {
mid, _ = _mid.(int64)
}
ctx.JSON(fmt.Sprintf("%d", mid), nil)
})
e.GET("/guest/mobile", authn.GuestMobile, func(ctx *bm.Context) {
var (
mid int64
)
if _mid, ok := ctx.Get("mid"); ok {
mid, _ = _mid.(int64)
}
ctx.JSON(fmt.Sprintf("%d", mid), nil)
})
e.POST("/guest/csrf", authn.Guest, func(ctx *bm.Context) {
var (
mid int64
)
if _mid, ok := ctx.Get("mid"); ok {
mid, _ = _mid.(int64)
}
ctx.JSON(fmt.Sprintf("%d", mid), nil)
})
return e
}
func TestFromNilConfig(t *testing.T) {
New(nil)
}
func TestIdentifyHandler(t *testing.T) {
e := engine()
go e.Run(":18080")
time.Sleep(time.Second)
// test cases
testWebUser(t, "/user")
testWebUser(t, "/metadata/user")
testWebUser(t, "/web")
testWebUser(t, "/guest")
testWebUser(t, "/guest/web")
testWebUserFailed(t, "/user")
testWebUserFailed(t, "/web")
testMobileUser(t, "/user")
testMobileUser(t, "/mobile")
testMobileUser(t, "/metadata/mobile")
testMobileUser(t, "/guest")
testMobileUser(t, "/guest/mobile")
testMobileUserFailed(t, "/user")
testMobileUserFailed(t, "/mobile")
testGuest(t, "/guest")
testGuestCSRF(t, "/guest/csrf")
testGuestCSRFFailed(t, "/guest/csrf")
testMultipartCSRF(t, "/guest/csrf")
if err := e.Server().Shutdown(context.TODO()); err != nil {
t.Logf("Failed to shutdown bm engine: %v", err)
}
}
func testWebUser(t *testing.T, path string) {
res := Response{}
query := url.Values{}
cli := client()
req, err := cli.NewRequest(http.MethodGet, "http://127.0.0.1:18080/"+path, "", query)
assert.NoError(t, err)
req.AddCookie(&http.Cookie{
Name: "DedeUserID",
Value: _testUID,
})
req.AddCookie(&http.Cookie{
Name: "DedeUserID__ckMd5",
Value: "36976f7a5cb6e4a6",
})
req.AddCookie(&http.Cookie{
Name: "SESSDATA",
Value: "7bf20cf0%2C1540627371%2C8ec39f0c",
})
err = cli.Do(context.TODO(), req, &res)
assert.NoError(t, err)
assert.Equal(t, 0, res.Code)
assert.Equal(t, _testUID, res.Data)
}
func testMobileUser(t *testing.T, path string) {
res := Response{}
query := url.Values{}
query.Set("access_key", "cdbd166be6673a5a4f6fbcdd88569edf")
cli := client()
req, err := cli.NewRequest(http.MethodGet, "http://127.0.0.1:18080"+path, "", query)
assert.NoError(t, err)
err = cli.Do(context.TODO(), req, &res)
assert.NoError(t, err)
assert.Equal(t, 0, res.Code)
assert.Equal(t, _testUID, res.Data)
}
func testWebUserFailed(t *testing.T, path string) {
res := Response{}
query := url.Values{}
cli := client()
req, err := cli.NewRequest(http.MethodGet, "http://127.0.0.1:18080/"+path, "", query)
assert.NoError(t, err)
req.AddCookie(&http.Cookie{
Name: "DedeUserID",
Value: _testUID,
})
req.AddCookie(&http.Cookie{
Name: "DedeUserID__ckMd5",
Value: "53c4b106fb4462f1",
})
req.AddCookie(&http.Cookie{
Name: "SESSDATA",
Value: "6eeda532%2C1515837495%2C5a6baa4e",
})
err = cli.Do(context.TODO(), req, &res)
assert.NoError(t, err)
assert.Equal(t, ecode.NoLogin.Code(), res.Code)
assert.Empty(t, res.Data)
}
func testMobileUserFailed(t *testing.T, path string) {
res := Response{}
query := url.Values{}
query.Set("access_key", "5dce488c2ff8d62d7b131da40ae18729")
cli := client()
req, err := cli.NewRequest(http.MethodGet, "http://127.0.0.1:18080"+path, "", query)
assert.NoError(t, err)
err = cli.Do(context.TODO(), req, &res)
assert.NoError(t, err)
assert.Equal(t, ecode.NoLogin.Code(), res.Code)
assert.Empty(t, res.Data)
}
func testGuest(t *testing.T, path string) {
res := Response{}
cli := client()
req, err := http.NewRequest(http.MethodGet, "http://127.0.0.1:18080"+path, nil)
assert.NoError(t, err)
err = cli.Do(context.TODO(), req, &res)
assert.NoError(t, err)
assert.Equal(t, 0, res.Code)
assert.Equal(t, "0", res.Data)
}
func testGuestCSRF(t *testing.T, path string) {
res := Response{}
param := url.Values{}
param.Set("csrf", "c1524bbf3aa5a1996ff7b1f29a09e796")
cli := client()
req, err := cli.NewRequest(http.MethodPost, "http://127.0.0.1:18080"+path, "", param)
assert.NoError(t, err)
req.AddCookie(&http.Cookie{
Name: "DedeUserID",
Value: _testUID,
})
req.AddCookie(&http.Cookie{
Name: "DedeUserID__ckMd5",
Value: "36976f7a5cb6e4a6",
})
req.AddCookie(&http.Cookie{
Name: "SESSDATA",
Value: "7bf20cf0%2C1540627371%2C8ec39f0c",
})
err = cli.Do(context.TODO(), req, &res)
assert.NoError(t, err)
assert.Equal(t, 0, res.Code)
assert.Equal(t, _testUID, res.Data)
}
func testGuestCSRFFailed(t *testing.T, path string) {
res := Response{}
param := url.Values{}
param.Set("csrf", "invalid-csrf-token")
cli := client()
req, err := cli.NewRequest(http.MethodPost, "http://127.0.0.1:18080"+path, "", param)
assert.NoError(t, err)
req.AddCookie(&http.Cookie{
Name: "DedeUserID",
Value: _testUID,
})
req.AddCookie(&http.Cookie{
Name: "DedeUserID__ckMd5",
Value: "36976f7a5cb6e4a6",
})
req.AddCookie(&http.Cookie{
Name: "SESSDATA",
Value: "7bf20cf0%2C1540627371%2C8ec39f0c",
})
err = cli.Do(context.TODO(), req, &res)
assert.NoError(t, err)
assert.Equal(t, ecode.CsrfNotMatchErr.Code(), res.Code)
assert.Empty(t, res.Data)
}
func testMultipartCSRF(t *testing.T, path string) {
res := Response{}
body := &bytes.Buffer{}
writer := multipart.NewWriter(body)
writer.WriteField("csrf", "c1524bbf3aa5a1996ff7b1f29a09e796")
writer.Close()
req, err := http.NewRequest("POST", "http://127.0.0.1:18080"+path, body)
assert.NoError(t, err)
req.Header.Set("Content-Type", writer.FormDataContentType())
cli := client()
req.AddCookie(&http.Cookie{
Name: "DedeUserID",
Value: _testUID,
})
req.AddCookie(&http.Cookie{
Name: "DedeUserID__ckMd5",
Value: "36976f7a5cb6e4a6",
})
req.AddCookie(&http.Cookie{
Name: "SESSDATA",
Value: "7bf20cf0%2C1540627371%2C8ec39f0c",
})
err = cli.Do(context.TODO(), req, &res)
assert.NoError(t, err)
assert.Equal(t, 0, res.Code)
assert.Equal(t, _testUID, res.Data)
}

View File

@@ -0,0 +1,45 @@
package auth_test
import (
"fmt"
bm "go-common/library/net/http/blademaster"
"go-common/library/net/http/blademaster/middleware/auth"
"go-common/library/net/metadata"
"go-common/library/net/rpc/warden"
)
// 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() {
authn := auth.New(&auth.Config{
Identify: &warden.ClientConfig{},
DisableCSRF: false,
})
e := bm.DefaultServer(nil)
// mark `/user` path as User policy
e.GET("/user", authn.User, func(ctx *bm.Context) {
mid := metadata.Int64(ctx, metadata.Mid)
ctx.JSON(fmt.Sprintf("%d", mid), nil)
})
// mark `/mobile` path as UserMobile policy
e.GET("/mobile", authn.UserMobile, func(ctx *bm.Context) {
mid := metadata.Int64(ctx, metadata.Mid)
ctx.JSON(fmt.Sprintf("%d", mid), nil)
})
// mark `/web` path as UserWeb policy
e.GET("/web", authn.UserWeb, func(ctx *bm.Context) {
mid := metadata.Int64(ctx, metadata.Mid)
ctx.JSON(fmt.Sprintf("%d", mid), nil)
})
// mark `/guest` path as Guest policy
e.GET("/guest", authn.Guest, func(ctx *bm.Context) {
mid := metadata.Int64(ctx, metadata.Mid)
ctx.JSON(fmt.Sprintf("%d", mid), nil)
})
e.Run(":18080")
}