You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
177 lines
3.8 KiB
177 lines
3.8 KiB
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 |
|
} |
|
}
|
|
|