bilibili-backup/library/net/http/blademaster/middleware/auth/auth.go
2019-04-22 02:59:20 +00:00

178 lines
3.8 KiB
Go

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