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,74 @@
package(default_visibility = ["//visibility:public"])
load(
"@io_bazel_rules_go//go:def.bzl",
"go_test",
"go_library",
)
go_test(
name = "go_default_test",
srcs = ["permit_test.go"],
embed = [":go_default_library"],
rundir = ".",
tags = ["automanaged"],
deps = [
"//library/cache/memcache:go_default_library",
"//library/container/pool:go_default_library",
"//library/ecode:go_default_library",
"//library/log:go_default_library",
"//library/net/http/blademaster:go_default_library",
"//library/net/netutil/breaker:go_default_library",
"//library/time:go_default_library",
],
)
go_library(
name = "go_default_library",
srcs = [
"permit.go",
"session.go",
],
importpath = "go-common/library/net/http/blademaster/middleware/permit",
tags = ["automanaged"],
visibility = ["//visibility:public"],
deps = [
"//app/admin/main/manager/api:go_default_library",
"//library/cache/memcache:go_default_library",
"//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/rpc/warden:go_default_library",
"//vendor/github.com/pkg/errors:go_default_library",
],
)
go_test(
name = "go_default_xtest",
srcs = ["example_test.go"],
tags = ["automanaged"],
deps = [
"//library/cache/memcache:go_default_library",
"//library/container/pool:go_default_library",
"//library/net/http/blademaster:go_default_library",
"//library/net/http/blademaster/middleware/permit:go_default_library",
"//library/net/metadata:go_default_library",
"//library/net/netutil/breaker: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,24 @@
### business/blademaster/permit
### Version 1.0.6
1. fix manager用户不存在的情况
### Version 1.0.5
1. permit无配置化
### Version 1.0.4
1. auth添加默认配置
### Version 1.0.3
1. auth去掉写cookie操作
##### Version 1.0.2
1. 修复dashboard auth cookie校验username问题
##### Version 1.0.1
1. auth后将username写入context
##### Version 1.0.0
1. 使用 dashbaord 来认证用户使用manager来获取用户权限使用 memcache 来缓存用户Session
2. 完成基本功能与测试

View File

@@ -0,0 +1,13 @@
#### business/blademaster/identify
##### 项目简介
blademaster 的 auth middleware主要用于设置路由的后台管理系统的登陆验证和鉴权策略
##### 编译环境
- **请只用 Golang v1.8.x 以上版本编译执行**
##### 依赖包
- No other dependency

View File

@@ -0,0 +1,101 @@
package permit_test
import (
"fmt"
"time"
"go-common/library/cache/memcache"
"go-common/library/container/pool"
bm "go-common/library/net/http/blademaster"
"go-common/library/net/http/blademaster/middleware/permit"
"go-common/library/net/metadata"
"go-common/library/net/netutil/breaker"
xtime "go-common/library/time"
)
// This example create a permit middleware instance and attach to several path,
// it will validate request by specified policy and put extra information into context. e.g., `uid`.
// It provides additional handler functions to provide the identification for your business handler.
func Example() {
a := permit.New(&permit.Config{
DsHTTPClient: &bm.ClientConfig{
App: &bm.App{
Key: "manager-go",
Secret: "949bbb2dd3178252638c2407578bc7ad",
},
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,
},
},
MaHTTPClient: &bm.ClientConfig{
App: &bm.App{
Key: "f6433799dbd88751",
Secret: "36f8ddb1806207fe07013ab6a77a3935",
},
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,
},
},
Session: &permit.SessionConfig{
SessionIDLength: 32,
CookieLifeTime: 1800,
CookieName: "mng-go",
Domain: ".bilibili.co",
Memcache: &memcache.Config{
Config: &pool.Config{
Active: 10,
Idle: 5,
IdleTimeout: xtime.Duration(time.Second * 80),
},
Name: "go-business/permit",
Proto: "tcp",
Addr: "172.16.33.54:11211",
DialTimeout: xtime.Duration(time.Millisecond * 1000),
ReadTimeout: xtime.Duration(time.Millisecond * 1000),
WriteTimeout: xtime.Duration(time.Millisecond * 1000),
},
},
ManagerHost: "http://uat-manager.bilibili.co",
DashboardHost: "http://uat-dashboard-mng.bilibili.co",
DashboardCaller: "manager-go",
})
p := permit.New2(nil)
e := bm.NewServer(nil)
//Check whether the user has logged in
e.GET("/login", a.Verify(), func(c *bm.Context) {
c.JSON("pass", nil)
})
//Check whether the user has logged in,and check th user has the access permisson to the specifed path
e.GET("/tag/del", a.Permit("TAG_DEL"), func(c *bm.Context) {
uid := metadata.Int64(c, metadata.Uid)
username := metadata.String(c, metadata.Username)
c.JSON(fmt.Sprintf("pass uid(%d) username(%s)", uid, username), nil)
})
e.GET("/check/login", p.Verify2(), func(c *bm.Context) {
c.JSON("pass", nil)
})
e.POST("/tag/del", p.Permit2("TAG_DEL"), func(c *bm.Context) {
uid := metadata.Int64(c, metadata.Uid)
username := metadata.String(c, metadata.Username)
c.JSON(fmt.Sprintf("pass uid(%d) username(%s)", uid, username), nil)
})
e.Run(":18080")
}

View File

@@ -0,0 +1,321 @@
package permit
import (
"net/url"
mng "go-common/app/admin/main/manager/api"
"go-common/library/ecode"
"go-common/library/log"
bm "go-common/library/net/http/blademaster"
"go-common/library/net/metadata"
"go-common/library/net/rpc/warden"
"github.com/pkg/errors"
)
const (
_verifyURI = "/api/session/verify"
_permissionURI = "/x/admin/manager/permission"
_sessIDKey = "_AJSESSIONID"
_sessUIDKey = "uid" // manager user_id
_sessUnKey = "username" // LDAP username
_defaultDomain = ".bilibili.co"
_defaultCookieName = "mng-go"
_defaultCookieLifeTime = 2592000
// CtxPermissions will be set into ctx.
CtxPermissions = "permissions"
)
// permissions .
type permissions struct {
UID int64 `json:"uid"`
Perms []string `json:"perms"`
}
// Permit is an auth middleware.
type Permit struct {
verifyURI string
permissionURI string
dashboardCaller string
dsClient *bm.Client // dashboard client
maClient *bm.Client // manager-admin client
sm *SessionManager // user Session
mng.PermitClient // mng grpc client
}
//Verify only export Verify function because of less configure
type Verify interface {
Verify() bm.HandlerFunc
}
// Config identify config.
type Config struct {
DsHTTPClient *bm.ClientConfig // dashboard client config. appkey can not reuse.
MaHTTPClient *bm.ClientConfig // manager-admin client config
Session *SessionConfig
ManagerHost string
DashboardHost string
DashboardCaller string
}
// Config2 .
type Config2 struct {
MngClient *warden.ClientConfig
Session *SessionConfig
}
// New new an auth service.
func New(c *Config) *Permit {
a := &Permit{
dashboardCaller: c.DashboardCaller,
verifyURI: c.DashboardHost + _verifyURI,
permissionURI: c.ManagerHost + _permissionURI,
dsClient: bm.NewClient(c.DsHTTPClient),
maClient: bm.NewClient(c.MaHTTPClient),
sm: newSessionManager(c.Session),
}
return a
}
// New2 .
func New2(c *warden.ClientConfig) *Permit {
permitClient, err := mng.NewClient(c)
if err != nil {
panic(errors.WithMessage(err, "Failed to dial mng rpc server"))
}
return &Permit{
PermitClient: permitClient,
sm: &SessionManager{},
}
}
// NewVerify new a verify service.
func NewVerify(c *Config) Verify {
a := &Permit{
verifyURI: c.DashboardHost + _verifyURI,
dsClient: bm.NewClient(c.DsHTTPClient),
dashboardCaller: c.DashboardCaller,
sm: newSessionManager(c.Session),
}
return a
}
// Verify2 check whether the user has logged in.
func (p *Permit) Verify2() bm.HandlerFunc {
return func(ctx *bm.Context) {
sid, username, err := p.login2(ctx)
if err != nil {
ctx.JSON(nil, ecode.Unauthorized)
ctx.Abort()
return
}
ctx.Set(_sessUnKey, username)
p.sm.setHTTPCookie(ctx, _defaultCookieName, sid)
}
}
// Verify return bm HandlerFunc which check whether the user has logged in.
func (p *Permit) Verify() bm.HandlerFunc {
return func(ctx *bm.Context) {
si, err := p.login(ctx)
if err != nil {
ctx.JSON(nil, ecode.Unauthorized)
ctx.Abort()
return
}
p.sm.SessionRelease(ctx, si)
}
}
// Permit return bm HandlerFunc which check whether the user has logged in and has the access permission of the location.
// If `permit` is empty,it will allow any logged in request.
func (p *Permit) Permit(permit string) bm.HandlerFunc {
return func(ctx *bm.Context) {
var (
si *Session
uid int64
perms []string
err error
)
si, err = p.login(ctx)
if err != nil {
ctx.JSON(nil, ecode.Unauthorized)
ctx.Abort()
return
}
defer p.sm.SessionRelease(ctx, si)
uid, perms, err = p.permissions(ctx, si.Get(_sessUnKey).(string))
if err == nil {
si.Set(_sessUIDKey, uid)
ctx.Set(_sessUIDKey, uid)
if md, ok := metadata.FromContext(ctx); ok {
md[metadata.Uid] = uid
}
}
if len(perms) > 0 {
ctx.Set(CtxPermissions, perms)
}
if !p.permit(permit, perms) {
ctx.JSON(nil, ecode.AccessDenied)
ctx.Abort()
return
}
}
}
// login check whether the user has logged in.
func (p *Permit) login(ctx *bm.Context) (si *Session, err error) {
si = p.sm.SessionStart(ctx)
if si.Get(_sessUnKey) == nil {
var username string
if username, err = p.verify(ctx); err != nil {
return
}
si.Set(_sessUnKey, username)
}
ctx.Set(_sessUnKey, si.Get(_sessUnKey))
if md, ok := metadata.FromContext(ctx); ok {
md[metadata.Username] = si.Get(_sessUnKey)
}
return
}
// Permit2 same function as permit function but reply on grpc.
func (p *Permit) Permit2(permit string) bm.HandlerFunc {
return func(ctx *bm.Context) {
sid, username, err := p.login2(ctx)
if err != nil {
ctx.JSON(nil, ecode.Unauthorized)
ctx.Abort()
return
}
p.sm.setHTTPCookie(ctx, _defaultCookieName, sid)
ctx.Set(_sessUnKey, username)
if md, ok := metadata.FromContext(ctx); ok {
md[metadata.Username] = username
}
reply, err := p.Permissions(ctx, &mng.PermissionReq{Username: username})
if err != nil {
if ecode.NothingFound.Equal(err) && permit != "" {
ctx.JSON(nil, ecode.AccessDenied)
ctx.Abort()
}
return
}
ctx.Set(_sessUIDKey, reply.Uid)
if md, ok := metadata.FromContext(ctx); ok {
md[metadata.Uid] = reply.Uid
}
if len(reply.Perms) > 0 {
ctx.Set(CtxPermissions, reply.Perms)
}
if !p.permit(permit, reply.Perms) {
ctx.JSON(nil, ecode.AccessDenied)
ctx.Abort()
return
}
}
}
// login2 .
func (p *Permit) login2(ctx *bm.Context) (sid, uname string, err error) {
var dsbsid, mngsid string
dsbck, err := ctx.Request.Cookie(_sessIDKey)
if err == nil {
dsbsid = dsbck.Value
}
if dsbsid == "" {
err = ecode.Unauthorized
return
}
mngck, err := ctx.Request.Cookie(_defaultCookieName)
if err == nil {
mngsid = mngck.Value
}
reply, err := p.Login(ctx, &mng.LoginReq{Mngsid: mngsid, Dsbsid: dsbsid})
if err != nil {
log.Error("mng rpc Login error(%v)", err)
return
}
sid = reply.Sid
uname = reply.Username
return
}
func (p *Permit) verify(ctx *bm.Context) (username string, err error) {
var (
sid string
r = ctx.Request
)
session, err := r.Cookie(_sessIDKey)
if err == nil {
sid = session.Value
}
if sid == "" {
err = ecode.Unauthorized
return
}
username, err = p.verifyDashboard(ctx, sid)
return
}
// permit check whether user has the access permission of the location.
func (p *Permit) permit(permit string, permissions []string) bool {
if permit == "" {
return true
}
for _, p := range permissions {
if p == permit {
// access the permit
return true
}
}
return false
}
// verifyDashboard check whether the user is valid from Dashboard.
func (p *Permit) verifyDashboard(ctx *bm.Context, sid string) (username string, err error) {
params := url.Values{}
params.Set("session_id", sid)
params.Set("encrypt", "md5")
params.Set("caller", p.dashboardCaller)
var res struct {
Code int `json:"code"`
UserName string `json:"username"`
}
if err = p.dsClient.Get(ctx, p.verifyURI, metadata.String(ctx, metadata.RemoteIP), params, &res); err != nil {
log.Error("dashboard get verify Session url(%s) error(%v)", p.verifyURI+"?"+params.Encode(), err)
return
}
if ecode.Int(res.Code) != ecode.OK {
log.Error("dashboard get verify Session url(%s) error(%v)", p.verifyURI+"?"+params.Encode(), res.Code)
err = ecode.Int(res.Code)
return
}
username = res.UserName
return
}
// permissions get user's permisssions from manager-admin.
func (p *Permit) permissions(ctx *bm.Context, username string) (uid int64, perms []string, err error) {
params := url.Values{}
params.Set(_sessUnKey, username)
var res struct {
Code int `json:"code"`
Data permissions `json:"data"`
}
if err = p.maClient.Get(ctx, p.permissionURI, metadata.String(ctx, metadata.RemoteIP), params, &res); err != nil {
log.Error("dashboard get permissions url(%s) error(%v)", p.permissionURI+"?"+params.Encode(), err)
return
}
if ecode.Int(res.Code) != ecode.OK {
log.Error("dashboard get permissions url(%s) error(%v)", p.permissionURI+"?"+params.Encode(), res.Code)
err = ecode.Int(res.Code)
return
}
perms = res.Data.Perms
uid = res.Data.UID
return
}

View File

@@ -0,0 +1,294 @@
package permit
import (
"context"
"net/http"
"net/url"
"sync"
"testing"
"time"
"go-common/library/cache/memcache"
"go-common/library/container/pool"
"go-common/library/ecode"
"go-common/library/log"
bm "go-common/library/net/http/blademaster"
"go-common/library/net/netutil/breaker"
xtime "go-common/library/time"
)
var (
once sync.Once
)
type Response struct {
Code int `json:"code"`
Data string `json:"data"`
}
func init() {
log.Init(nil)
}
func client() *bm.Client {
return bm.NewClient(&bm.ClientConfig{
App: &bm.App{
Key: "test",
Secret: "test",
},
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 getPermit() *Permit {
return New(&Config{
DsHTTPClient: &bm.ClientConfig{
App: &bm.App{
Key: "manager-go",
Secret: "949bbb2dd3178252638c2407578bc7ad",
},
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,
},
},
MaHTTPClient: &bm.ClientConfig{
App: &bm.App{
Key: "f6433799dbd88751",
Secret: "36f8ddb1806207fe07013ab6a77a3935",
},
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,
},
},
Session: &SessionConfig{
SessionIDLength: 32,
CookieLifeTime: 1800,
CookieName: "mng-go",
Domain: ".bilibili.co",
Memcache: &memcache.Config{
Config: &pool.Config{
Active: 10,
Idle: 5,
IdleTimeout: xtime.Duration(time.Second * 80),
},
Name: "go-business/auth",
Proto: "tcp",
Addr: "172.16.33.54:11211",
DialTimeout: xtime.Duration(time.Millisecond * 1000),
ReadTimeout: xtime.Duration(time.Millisecond * 1000),
WriteTimeout: xtime.Duration(time.Millisecond * 1000),
},
},
ManagerHost: "http://uat-manager.bilibili.co",
DashboardHost: "http://dashboard-mng.bilibili.co",
DashboardCaller: "manager-go",
})
}
func engine() *bm.Engine {
e := bm.NewServer(nil)
a := getPermit()
e.GET("/login", a.Verify(), func(c *bm.Context) {
c.JSON("pass", nil)
})
e.GET("/tag/del", a.Permit("TAG_DEL"), func(c *bm.Context) {
c.JSON("pass", nil)
})
e.GET("/tag/admin", a.Permit("TAG_ADMIN"), func(c *bm.Context) {
c.JSON("pass", nil)
})
return e
}
func setSession(uid int64, username string) (string, error) {
a := getPermit()
sv := a.sm.newSession(context.TODO())
sv.Set("username", username)
mcConn := a.sm.mc.Get(context.TODO())
defer mcConn.Close()
key := sv.Sid
item := &memcache.Item{
Key: key,
Object: sv,
Flags: memcache.FlagJSON,
Expiration: int32(a.sm.c.CookieLifeTime),
}
if err := mcConn.Set(item); err != nil {
return "", err
}
return key, nil
}
func startEngine(t *testing.T) func() {
return func() {
e := engine()
err := e.Run(":18080")
if err != nil {
t.Fatalf("failed to run server!%v", err)
}
}
}
func TestLoginSuccess(t *testing.T) {
go once.Do(startEngine(t))
time.Sleep(time.Millisecond * 100)
sid, err := setSession(2233, "caoguoliang")
if err != nil {
t.Fatalf("faild to set session !err:=%v", err)
}
query := url.Values{}
query.Set("test", "test")
cli := client()
req, err := cli.NewRequest("GET", "http://127.0.0.1:18080/login", "", query)
if err != nil {
t.Fatalf("Failed to build request: %v", err)
}
req.AddCookie(&http.Cookie{
Name: "mng-go",
Value: sid,
})
req.AddCookie(&http.Cookie{
Name: "username",
Value: "caoguoliang",
})
req.AddCookie(&http.Cookie{
Name: "_AJSESSIONID",
Value: "87fa8450e93511e79ed8522233007f8a",
})
res := Response{}
if err := cli.Do(context.TODO(), req, &res); err != nil {
t.Fatalf("Failed to send request: %v", err)
}
if res.Code != 0 || res.Data != "pass" {
t.Fatalf("Unexpected response code(%d) data(%v)", res.Code, res.Data)
}
}
func TestLoginFail(t *testing.T) {
go once.Do(startEngine(t))
time.Sleep(time.Millisecond * 100)
query := url.Values{}
query.Set("test", "test")
cli := client()
req, err := cli.NewRequest("GET", "http://127.0.0.1:18080/login", "", query)
if err != nil {
t.Fatalf("Failed to build request: %v", err)
}
req.AddCookie(&http.Cookie{
Name: "mng-go",
Value: "fakesess",
})
req.AddCookie(&http.Cookie{
Name: "username",
Value: "caoguoliang",
})
req.AddCookie(&http.Cookie{
Name: "_AJSESSIONID",
Value: "testsess",
})
res := Response{}
if err := cli.Do(context.TODO(), req, &res); err != nil {
t.Fatalf("Failed to send request: %v", err)
}
if res.Code != ecode.Unauthorized.Code() {
t.Fatalf("This request should be forbidden: code(%d) data(%v)", res.Code, res.Data)
}
}
func TestVerifySuccess(t *testing.T) {
go once.Do(startEngine(t))
time.Sleep(time.Millisecond * 100)
sid, err := setSession(2233, "caoguoliang")
if err != nil {
t.Fatalf("faild to set session !err:=%v", err)
}
query := url.Values{}
query.Set("test", "test")
cli := client()
req, err := cli.NewRequest("GET", "http://127.0.0.1:18080/tag/del", "", query)
if err != nil {
t.Fatalf("Failed to build request: %v", err)
}
req.AddCookie(&http.Cookie{
Name: "mng-go",
Value: sid,
})
req.AddCookie(&http.Cookie{
Name: "username",
Value: "caoguoliang",
})
req.AddCookie(&http.Cookie{
Name: "_AJSESSIONID",
Value: "87fa8450e93511e79ed8522233007f8a",
})
res := Response{}
if err := cli.Do(context.TODO(), req, &res); err != nil {
t.Fatalf("Failed to send request: %v", err)
}
if res.Code != 0 || res.Data != "pass" {
t.Fatalf("Unexpected response code(%d) data(%v)", res.Code, res.Data)
}
}
func TestVerifyFail(t *testing.T) {
go once.Do(startEngine(t))
time.Sleep(time.Millisecond * 100)
sid, err := setSession(2233, "caoguoliang")
if err != nil {
t.Fatalf("faild to set session !err:=%v", err)
}
query := url.Values{}
query.Set("test", "test")
cli := client()
req, err := cli.NewRequest("GET", "http://127.0.0.1:18080/tag/admin", "", query)
if err != nil {
t.Fatalf("Failed to build request: %v", err)
}
req.AddCookie(&http.Cookie{
Name: "mng-go",
Value: sid,
})
req.AddCookie(&http.Cookie{
Name: "username",
Value: "caoguoliang",
})
req.AddCookie(&http.Cookie{
Name: "_AJSESSIONID",
Value: "87fa8450e93511e79ed8522233007f8a",
})
res := Response{}
if err := cli.Do(context.TODO(), req, &res); err != nil {
t.Fatalf("Failed to send request: %v", err)
}
if res.Code != ecode.AccessDenied.Code() {
t.Fatalf("This request should be forbidden: code(%d) data(%v)", res.Code, res.Data)
}
}

View File

@@ -0,0 +1,152 @@
package permit
import (
"context"
"crypto/rand"
"encoding/hex"
"net/http"
"net/url"
"sync"
"time"
"go-common/library/cache/memcache"
"go-common/library/log"
bm "go-common/library/net/http/blademaster"
)
// Session http session.
type Session struct {
Sid string
lock sync.RWMutex
Values map[string]interface{}
}
// SessionConfig config of Session.
type SessionConfig struct {
SessionIDLength int
CookieLifeTime int
CookieName string
Domain string
Memcache *memcache.Config
}
// SessionManager .
type SessionManager struct {
mc *memcache.Pool // Session cache
c *SessionConfig
}
// newSessionManager .
func newSessionManager(c *SessionConfig) (s *SessionManager) {
s = &SessionManager{
mc: memcache.NewPool(c.Memcache),
c: c,
}
return
}
// SessionStart start session.
func (s *SessionManager) SessionStart(ctx *bm.Context) (si *Session) {
// check manager Session id, if err or no exist need new one.
if si, _ = s.cache(ctx); si == nil {
si = s.newSession(ctx)
}
return
}
// SessionRelease flush session into store.
func (s *SessionManager) SessionRelease(ctx *bm.Context, sv *Session) {
// set http cookie
s.setHTTPCookie(ctx, s.c.CookieName, sv.Sid)
// set mc
conn := s.mc.Get(ctx)
defer conn.Close()
key := sv.Sid
item := &memcache.Item{
Key: key,
Object: sv,
Flags: memcache.FlagJSON,
Expiration: int32(s.c.CookieLifeTime),
}
if err := conn.Set(item); err != nil {
log.Error("SessionManager set error(%s,%v)", key, err)
}
}
// SessionDestroy destroy session.
func (s *SessionManager) SessionDestroy(ctx *bm.Context, sv *Session) {
conn := s.mc.Get(ctx)
defer conn.Close()
if err := conn.Delete(sv.Sid); err != nil {
log.Error("SessionManager delete error(%s,%v)", sv.Sid, err)
}
}
func (s *SessionManager) cache(ctx *bm.Context) (res *Session, err error) {
ck, err := ctx.Request.Cookie(s.c.CookieName)
if err != nil || ck == nil {
return
}
sid := ck.Value
// get from cache
conn := s.mc.Get(ctx)
defer conn.Close()
r, err := conn.Get(sid)
if err != nil {
if err == memcache.ErrNotFound {
err = nil
return
}
log.Error("conn.Get(%s) error(%v)", sid, err)
return
}
res = &Session{}
if err = conn.Scan(r, res); err != nil {
log.Error("conn.Scan(%v) error(%v)", string(r.Value), err)
}
return
}
func (s *SessionManager) newSession(ctx context.Context) (res *Session) {
b := make([]byte, s.c.SessionIDLength)
n, err := rand.Read(b)
if n != len(b) || err != nil {
return nil
}
res = &Session{
Sid: hex.EncodeToString(b),
Values: make(map[string]interface{}),
}
return
}
func (s *SessionManager) setHTTPCookie(ctx *bm.Context, name, value string) {
cookie := &http.Cookie{
Name: name,
Value: url.QueryEscape(value),
Path: "/",
HttpOnly: true,
Domain: _defaultDomain,
}
cookie.MaxAge = _defaultCookieLifeTime
cookie.Expires = time.Now().Add(time.Duration(_defaultCookieLifeTime) * time.Second)
http.SetCookie(ctx.Writer, cookie)
}
// Get get value by key.
func (s *Session) Get(key string) (value interface{}) {
s.lock.RLock()
defer s.lock.RUnlock()
value = s.Values[key]
return
}
// Set set value into session.
func (s *Session) Set(key string, value interface{}) (err error) {
s.lock.Lock()
defer s.lock.Unlock()
s.Values[key] = value
return
}