Updated session lib

This commit is contained in:
Torkel Ödegaard 2014-12-30 10:28:27 +01:00
parent e9fcca16bd
commit aafe2c5b98
19 changed files with 831 additions and 2205 deletions

2
Godeps/Godeps.json generated
View File

@ -30,7 +30,7 @@
},
{
"ImportPath": "github.com/macaron-contrib/session",
"Rev": "f00d48fd4f85088603c1493b0a99fdfe95d0658c"
"Rev": "65b8817c40cb5bdce08673a15fd2a648c2ba0e16"
},
{
"ImportPath": "github.com/mattn/go-sqlite3",

View File

@ -1,177 +1,16 @@
session
session [![Build Status](https://drone.io/github.com/macaron-contrib/session/status.png)](https://drone.io/github.com/macaron-contrib/session/latest) [![](http://gocover.io/_badge/github.com/macaron-contrib/session)](http://gocover.io/github.com/macaron-contrib/session)
=======
Middleware session is the session manager of [Macaron](https://github.com/Unknwon/macaron). It can use many session providers, including cookie, memory, file, redis, memcache, PostgreSQL, MySQL, and couchbase.
[API Reference](https://gowalker.org/github.com/macaron-contrib/session)
Middleware session provides session management for [Macaron](https://github.com/Unknwon/macaron). It can use many session providers, including memory, file, Redis, Memcache, PostgreSQL, MySQL, Couchbase and Ledis.
### Installation
go get github.com/macaron-contrib/session
## Usage
```go
import (
"github.com/Unknwon/macaron"
"github.com/macaron-contrib/session"
)
func main() {
m := macaron.Classic()
m.Use(session.Sessioner())
m.Get("/", func(sess session.Store) string {
sess.Set("session", "session middleware")
return sess.Get("session").(string)
})
m.Get("/signup", func(ctx *macaron.Context, f *session.Flash) {
f.Success("yes!!!")
f.Error("opps...")
// Use following fields in template
// - {{.Flash.SuccessMsg}}
// - {{.Flash.ErrorMsg}}
ctx.HTML(200, "signup", ctx.Data)
})
m.Run()
}
```
To use redis, memcache, PostgreSQL, MySQL, or couchbase as adapter, you should import their init functions:
```go
import (
_ "github.com/macaron-contrib/session/redis"
_ "github.com/macaron-contrib/session/memcache"
_ "github.com/macaron-contrib/session/postgres"
_ "github.com/macaron-contrib/session/mysql"
_ "github.com/macaron-contrib/session/couchbase"
)
```
## Options
`session.Sessioner` comes with a variety of configuration options:
```go
// ...
m.Use(session.Sessioner(session.Options{
Provider: "memory", // Name of provider.
Config: Config{
CookieName: "MacaronSession", // Key name store in cookie.
Gclifetime: 3600, // GC interval for memory adapter.
ProviderConfig: "./tmp", // Provider configuration string.
},
}))
// ...
```
### Example Options
- memory:
```go
// ...
m.Use(session.Sessioner(session.Options{
Provider: "memory", // Name of provider.
Config: Config{
CookieName: "MacaronSession", // Key name store in cookie.
Gclifetime: 3600, // GC interval for memory adapter.
ProviderConfig: "./tmp", // Provider configuration string.
},
}))
// ...
```
- file:
```go
// ...
m.Use(session.Sessioner(session.Options{
Provider: "file", // Name of provider.
Config: Config{
CookieName: "MacaronSession", // Key name store in cookie.
Gclifetime: 3600, // GC interval for memory adapter.
ProviderConfig: "./tmp", // Provider configuration string.
},
}))
// ...
```
- Redis:
```go
// ...
m.Use(session.Sessioner(session.Options{
Provider: "redis", // Name of provider.
Config: Config{
CookieName: "MacaronSession", // Key name store in cookie.
Gclifetime: 3600, // GC interval for memory adapter.
ProviderConfig: "127.0.0.1:6379,100,macaron", // Provider configuration string.
},
}))
// ...
```
- MySQL:
```go
// ...
m.Use(session.Sessioner(session.Options{
Provider: "mysql", // Name of provider.
Config: Config{
CookieName: "MacaronSession", // Key name store in cookie.
Gclifetime: 3600, // GC interval for memory adapter.
ProviderConfig: "username:password@protocol(address)/dbname?param=value", // Provider configuration string.
},
}))
// ...
```
- Cookie:
```go
// ...
m.Use(session.Sessioner(session.Options{
Provider: "cookie", // Name of provider.
Config: Config{
CookieName: "MacaronSession", // Key name store in cookie.
Gclifetime: 3600, // GC interval for memory adapter.
ProviderConfig: "{\"cookieName\":\"gosessionid\",\"securityKey\":\"beegocookiehashkey\"}", // Provider configuration string.
},
}))
// ...
```
## How to write own provider?
When you develop a web app, maybe you want to write own provider because you must meet the requirements.
Writing a provider is easy. You only need to define two struct types
(Session and Provider), which satisfy the interface definition.
Maybe you will find the **memory** provider is a good example.
type Store interface {
Set(key, value interface{}) error //set session value
Get(key interface{}) interface{} //get session value
Delete(key interface{}) error //delete session value
SessionID() string //back current sessionID
SessionRelease(w http.ResponseWriter) // release the resource & save data to provider & return the data
Flush() error //delete all data
}
type Provider interface {
SessionInit(gclifetime int64, config string) error
SessionRead(sid string) (SessionStore, error)
SessionExist(sid string) bool
SessionRegenerate(oldsid, sid string) (Store, error)
SessionDestroy(sid string) error
SessionAll() int //get all active session
SessionGC()
}
## Getting Help
- [API Reference](https://gowalker.org/github.com/macaron-contrib/session)
- [Documentation](http://macaron.gogs.io/docs/middlewares/session)
## License

View File

@ -16,7 +16,6 @@
package session
import (
"net/http"
"strings"
"sync"
@ -25,72 +24,78 @@ import (
"github.com/macaron-contrib/session"
)
var couchbpder = &CouchbaseProvider{}
// CouchbaseSessionStore represents a couchbase session store implementation.
type CouchbaseSessionStore struct {
b *couchbase.Bucket
sid string
lock sync.RWMutex
values map[interface{}]interface{}
data map[interface{}]interface{}
maxlifetime int64
}
// Set sets value to given key in session.
func (s *CouchbaseSessionStore) Set(key, val interface{}) error {
s.lock.Lock()
defer s.lock.Unlock()
s.data[key] = val
return nil
}
// Get gets value by given key in session.
func (s *CouchbaseSessionStore) Get(key interface{}) interface{} {
s.lock.RLock()
defer s.lock.RUnlock()
return s.data[key]
}
// Delete delete a key from session.
func (s *CouchbaseSessionStore) Delete(key interface{}) error {
s.lock.Lock()
defer s.lock.Unlock()
delete(s.data, key)
return nil
}
// ID returns current session ID.
func (s *CouchbaseSessionStore) ID() string {
return s.sid
}
// Release releases resource and save data to provider.
func (s *CouchbaseSessionStore) Release() error {
defer s.b.Close()
data, err := session.EncodeGob(s.data)
if err != nil {
return err
}
return s.b.Set(s.sid, int(s.maxlifetime), data)
}
// Flush deletes all session data.
func (s *CouchbaseSessionStore) Flush() error {
s.lock.Lock()
defer s.lock.Unlock()
s.data = make(map[interface{}]interface{})
return nil
}
// CouchbaseProvider represents a couchbase session provider implementation.
type CouchbaseProvider struct {
maxlifetime int64
savePath string
connStr string
pool string
bucket string
b *couchbase.Bucket
}
func (cs *CouchbaseSessionStore) Set(key, value interface{}) error {
cs.lock.Lock()
defer cs.lock.Unlock()
cs.values[key] = value
return nil
}
func (cs *CouchbaseSessionStore) Get(key interface{}) interface{} {
cs.lock.RLock()
defer cs.lock.RUnlock()
if v, ok := cs.values[key]; ok {
return v
} else {
return nil
}
}
func (cs *CouchbaseSessionStore) Delete(key interface{}) error {
cs.lock.Lock()
defer cs.lock.Unlock()
delete(cs.values, key)
return nil
}
func (cs *CouchbaseSessionStore) Flush() error {
cs.lock.Lock()
defer cs.lock.Unlock()
cs.values = make(map[interface{}]interface{})
return nil
}
func (cs *CouchbaseSessionStore) SessionID() string {
return cs.sid
}
func (cs *CouchbaseSessionStore) SessionRelease(w http.ResponseWriter) {
defer cs.b.Close()
bo, err := session.EncodeGob(cs.values)
if err != nil {
return
}
cs.b.Set(cs.sid, int(cs.maxlifetime), bo)
}
func (cp *CouchbaseProvider) getBucket() *couchbase.Bucket {
c, err := couchbase.Connect(cp.savePath)
c, err := couchbase.Connect(cp.connStr)
if err != nil {
return nil
}
@ -108,32 +113,32 @@ func (cp *CouchbaseProvider) getBucket() *couchbase.Bucket {
return bucket
}
// init couchbase session
// savepath like couchbase server REST/JSON URL
// Init initializes memory session provider.
// connStr is couchbase server REST/JSON URL
// e.g. http://host:port/, Pool, Bucket
func (cp *CouchbaseProvider) SessionInit(maxlifetime int64, savePath string) error {
cp.maxlifetime = maxlifetime
configs := strings.Split(savePath, ",")
func (p *CouchbaseProvider) Init(maxlifetime int64, connStr string) error {
p.maxlifetime = maxlifetime
configs := strings.Split(connStr, ",")
if len(configs) > 0 {
cp.savePath = configs[0]
p.connStr = configs[0]
}
if len(configs) > 1 {
cp.pool = configs[1]
p.pool = configs[1]
}
if len(configs) > 2 {
cp.bucket = configs[2]
p.bucket = configs[2]
}
return nil
}
// read couchbase session by sid
func (cp *CouchbaseProvider) SessionRead(sid string) (session.RawStore, error) {
cp.b = cp.getBucket()
// Read returns raw session store by session ID.
func (p *CouchbaseProvider) Read(sid string) (session.RawStore, error) {
p.b = p.getBucket()
var doc []byte
err := cp.b.Get(sid, &doc)
err := p.b.Get(sid, &doc)
var kv map[interface{}]interface{}
if doc == nil {
kv = make(map[interface{}]interface{})
@ -144,38 +149,49 @@ func (cp *CouchbaseProvider) SessionRead(sid string) (session.RawStore, error) {
}
}
cs := &CouchbaseSessionStore{b: cp.b, sid: sid, values: kv, maxlifetime: cp.maxlifetime}
cs := &CouchbaseSessionStore{b: p.b, sid: sid, data: kv, maxlifetime: p.maxlifetime}
return cs, nil
}
func (cp *CouchbaseProvider) SessionExist(sid string) bool {
cp.b = cp.getBucket()
defer cp.b.Close()
// Exist returns true if session with given ID exists.
func (p *CouchbaseProvider) Exist(sid string) bool {
p.b = p.getBucket()
defer p.b.Close()
var doc []byte
if err := cp.b.Get(sid, &doc); err != nil || doc == nil {
if err := p.b.Get(sid, &doc); err != nil || doc == nil {
return false
} else {
return true
}
}
func (cp *CouchbaseProvider) SessionRegenerate(oldsid, sid string) (session.RawStore, error) {
cp.b = cp.getBucket()
// Destory deletes a session by session ID.
func (p *CouchbaseProvider) Destory(sid string) error {
p.b = p.getBucket()
defer p.b.Close()
p.b.Delete(sid)
return nil
}
// Regenerate regenerates a session store from old session ID to new one.
func (p *CouchbaseProvider) Regenerate(oldsid, sid string) (session.RawStore, error) {
p.b = p.getBucket()
var doc []byte
if err := cp.b.Get(oldsid, &doc); err != nil || doc == nil {
cp.b.Set(sid, int(cp.maxlifetime), "")
if err := p.b.Get(oldsid, &doc); err != nil || doc == nil {
p.b.Set(sid, int(p.maxlifetime), "")
} else {
err := cp.b.Delete(oldsid)
err := p.b.Delete(oldsid)
if err != nil {
return nil, err
}
_, _ = cp.b.Add(sid, int(cp.maxlifetime), doc)
_, _ = p.b.Add(sid, int(p.maxlifetime), doc)
}
err := cp.b.Get(sid, &doc)
err := p.b.Get(sid, &doc)
if err != nil {
return nil, err
}
@ -189,26 +205,19 @@ func (cp *CouchbaseProvider) SessionRegenerate(oldsid, sid string) (session.RawS
}
}
cs := &CouchbaseSessionStore{b: cp.b, sid: sid, values: kv, maxlifetime: cp.maxlifetime}
cs := &CouchbaseSessionStore{b: p.b, sid: sid, data: kv, maxlifetime: p.maxlifetime}
return cs, nil
}
func (cp *CouchbaseProvider) SessionDestroy(sid string) error {
cp.b = cp.getBucket()
defer cp.b.Close()
cp.b.Delete(sid)
return nil
}
func (cp *CouchbaseProvider) SessionGC() {
return
}
func (cp *CouchbaseProvider) SessionAll() int {
// Count counts and returns number of sessions.
func (p *CouchbaseProvider) Count() int {
// FIXME
return 0
}
// GC calls GC to clean expired sessions.
func (p *CouchbaseProvider) GC() {}
func init() {
session.Register("couchbase", couchbpder)
session.Register("couchbase", &CouchbaseProvider{})
}

View File

@ -16,89 +16,89 @@
package session
import (
"net/http"
"sync"
"github.com/astaxie/beego/session"
"github.com/siddontang/ledisdb/config"
"github.com/siddontang/ledisdb/ledis"
"github.com/macaron-contrib/session"
)
var ledispder = &LedisProvider{}
var c *ledis.DB
// ledis session store
// LedisSessionStore represents a ledis session store implementation.
type LedisSessionStore struct {
sid string
lock sync.RWMutex
values map[interface{}]interface{}
data map[interface{}]interface{}
maxlifetime int64
}
// set value in ledis session
func (ls *LedisSessionStore) Set(key, value interface{}) error {
ls.lock.Lock()
defer ls.lock.Unlock()
ls.values[key] = value
// Set sets value to given key in session.
func (s *LedisSessionStore) Set(key, val interface{}) error {
s.lock.Lock()
defer s.lock.Unlock()
s.data[key] = val
return nil
}
// get value in ledis session
func (ls *LedisSessionStore) Get(key interface{}) interface{} {
ls.lock.RLock()
defer ls.lock.RUnlock()
if v, ok := ls.values[key]; ok {
return v
} else {
return nil
}
// Get gets value by given key in session.
func (s *LedisSessionStore) Get(key interface{}) interface{} {
s.lock.RLock()
defer s.lock.RUnlock()
return s.data[key]
}
// delete value in ledis session
func (ls *LedisSessionStore) Delete(key interface{}) error {
ls.lock.Lock()
defer ls.lock.Unlock()
delete(ls.values, key)
// Delete delete a key from session.
func (s *LedisSessionStore) Delete(key interface{}) error {
s.lock.Lock()
defer s.lock.Unlock()
delete(s.data, key)
return nil
}
// clear all values in ledis session
func (ls *LedisSessionStore) Flush() error {
ls.lock.Lock()
defer ls.lock.Unlock()
ls.values = make(map[interface{}]interface{})
return nil
// ID returns current session ID.
func (s *LedisSessionStore) ID() string {
return s.sid
}
// get ledis session id
func (ls *LedisSessionStore) SessionID() string {
return ls.sid
}
// save session values to ledis
func (ls *LedisSessionStore) SessionRelease(w http.ResponseWriter) {
b, err := session.EncodeGob(ls.values)
// Release releases resource and save data to provider.
func (s *LedisSessionStore) Release() error {
data, err := session.EncodeGob(s.data)
if err != nil {
return
return err
}
c.Set([]byte(ls.sid), b)
c.Expire([]byte(ls.sid), ls.maxlifetime)
if err = c.Set([]byte(s.sid), data); err != nil {
return err
}
_, err = c.Expire([]byte(s.sid), s.maxlifetime)
return err
}
// ledis session provider
// Flush deletes all session data.
func (s *LedisSessionStore) Flush() error {
s.lock.Lock()
defer s.lock.Unlock()
s.data = make(map[interface{}]interface{})
return nil
}
// LedisProvider represents a ledis session provider implementation.
type LedisProvider struct {
maxlifetime int64
savePath string
}
// init ledis session
// savepath like ledis server saveDataPath,pool size
// e.g. 127.0.0.1:6379,100,astaxie
func (lp *LedisProvider) SessionInit(maxlifetime int64, savePath string) error {
lp.maxlifetime = maxlifetime
lp.savePath = savePath
// Init initializes memory session provider.
func (p *LedisProvider) Init(maxlifetime int64, savePath string) error {
p.maxlifetime = maxlifetime
p.savePath = savePath
cfg := new(config.Config)
cfg.DataDir = lp.savePath
cfg.DataDir = p.savePath
var err error
nowLedis, err := ledis.Open(cfg)
c, err = nowLedis.Select(0)
@ -109,8 +109,8 @@ func (lp *LedisProvider) SessionInit(maxlifetime int64, savePath string) error {
return nil
}
// read ledis session by sid
func (lp *LedisProvider) SessionRead(sid string) (session.SessionStore, error) {
// Read returns raw session store by session ID.
func (p *LedisProvider) Read(sid string) (session.RawStore, error) {
kvs, err := c.Get([]byte(sid))
var kv map[interface{}]interface{}
if len(kvs) == 0 {
@ -121,12 +121,12 @@ func (lp *LedisProvider) SessionRead(sid string) (session.SessionStore, error) {
return nil, err
}
}
ls := &LedisSessionStore{sid: sid, values: kv, maxlifetime: lp.maxlifetime}
ls := &LedisSessionStore{sid: sid, data: kv, maxlifetime: p.maxlifetime}
return ls, nil
}
// check ledis session exist by sid
func (lp *LedisProvider) SessionExist(sid string) bool {
// Exist returns true if session with given ID exists.
func (p *LedisProvider) Exist(sid string) bool {
count, _ := c.Exists([]byte(sid))
if count == 0 {
return false
@ -135,19 +135,25 @@ func (lp *LedisProvider) SessionExist(sid string) bool {
}
}
// generate new sid for ledis session
func (lp *LedisProvider) SessionRegenerate(oldsid, sid string) (session.SessionStore, error) {
// Destory deletes a session by session ID.
func (p *LedisProvider) Destory(sid string) error {
_, err := c.Del([]byte(sid))
return err
}
// Regenerate regenerates a session store from old session ID to new one.
func (p *LedisProvider) Regenerate(oldsid, sid string) (session.RawStore, error) {
count, _ := c.Exists([]byte(sid))
if count == 0 {
// oldsid doesn't exists, set the new sid directly
// ignore error here, since if it return error
// the existed value will be 0
c.Set([]byte(sid), []byte(""))
c.Expire([]byte(sid), lp.maxlifetime)
c.Expire([]byte(sid), p.maxlifetime)
} else {
data, _ := c.Get([]byte(oldsid))
c.Set([]byte(sid), data)
c.Expire([]byte(sid), lp.maxlifetime)
c.Expire([]byte(sid), p.maxlifetime)
}
kvs, err := c.Get([]byte(sid))
var kv map[interface{}]interface{}
@ -159,26 +165,19 @@ func (lp *LedisProvider) SessionRegenerate(oldsid, sid string) (session.SessionS
return nil, err
}
}
ls := &LedisSessionStore{sid: sid, values: kv, maxlifetime: lp.maxlifetime}
ls := &LedisSessionStore{sid: sid, data: kv, maxlifetime: p.maxlifetime}
return ls, nil
}
// delete ledis session by id
func (lp *LedisProvider) SessionDestroy(sid string) error {
c.Del([]byte(sid))
return nil
}
// Impelment method, no used.
func (lp *LedisProvider) SessionGC() {
return
}
// @todo
func (lp *LedisProvider) SessionAll() int {
// Count counts and returns number of sessions.
func (p *LedisProvider) Count() int {
// FIXME
return 0
}
// GC calls GC to clean expired sessions.
func (p *LedisProvider) GC() {}
func init() {
session.Register("ledis", ledispder)
session.Register("ledis", &LedisProvider{})
}

View File

@ -16,7 +16,6 @@
package session
import (
"net/http"
"strings"
"sync"
@ -26,73 +25,72 @@ import (
)
var (
mempder = &MemProvider{}
client *memcache.Client
client *memcache.Client
)
// memcache session store
// MemcacheSessionStore represents a memcache session store implementation.
type MemcacheSessionStore struct {
sid string
lock sync.RWMutex
values map[interface{}]interface{}
data map[interface{}]interface{}
maxlifetime int64
}
// set value in memcache session
func (rs *MemcacheSessionStore) Set(key, value interface{}) error {
rs.lock.Lock()
defer rs.lock.Unlock()
rs.values[key] = value
// Set sets value to given key in session.
func (s *MemcacheSessionStore) Set(key, val interface{}) error {
s.lock.Lock()
defer s.lock.Unlock()
s.data[key] = val
return nil
}
// get value in memcache session
func (rs *MemcacheSessionStore) Get(key interface{}) interface{} {
rs.lock.RLock()
defer rs.lock.RUnlock()
if v, ok := rs.values[key]; ok {
return v
} else {
return nil
}
// Get gets value by given key in session.
func (s *MemcacheSessionStore) Get(key interface{}) interface{} {
s.lock.RLock()
defer s.lock.RUnlock()
return s.data[key]
}
// delete value in memcache session
func (rs *MemcacheSessionStore) Delete(key interface{}) error {
rs.lock.Lock()
defer rs.lock.Unlock()
delete(rs.values, key)
// Delete delete a key from session.
func (s *MemcacheSessionStore) Delete(key interface{}) error {
s.lock.Lock()
defer s.lock.Unlock()
delete(s.data, key)
return nil
}
// clear all values in memcache session
func (rs *MemcacheSessionStore) Flush() error {
rs.lock.Lock()
defer rs.lock.Unlock()
rs.values = make(map[interface{}]interface{})
return nil
// ID returns current session ID.
func (s *MemcacheSessionStore) ID() string {
return s.sid
}
// get redis session id
func (rs *MemcacheSessionStore) SessionID() string {
return rs.sid
}
// save session values to redis
func (rs *MemcacheSessionStore) SessionRelease(w http.ResponseWriter) {
b, err := session.EncodeGob(rs.values)
// Release releases resource and save data to provider.
func (s *MemcacheSessionStore) Release() error {
data, err := session.EncodeGob(s.data)
if err != nil {
return
return err
}
client.Set(&memcache.Item{
Key: rs.sid,
Value: b,
Expiration: int32(rs.maxlifetime),
return client.Set(&memcache.Item{
Key: s.sid,
Value: data,
Expiration: int32(s.maxlifetime),
})
}
// redis session provider
// Flush deletes all session data.
func (s *MemcacheSessionStore) Flush() error {
s.lock.Lock()
defer s.lock.Unlock()
s.data = make(map[interface{}]interface{})
return nil
}
// MemProvider represents a memcache session provider implementation.
type MemProvider struct {
maxlifetime int64
conninfo []string
@ -100,25 +98,25 @@ type MemProvider struct {
password string
}
// init redis session
// savepath like
// Init initializes memory session provider.
// connStrs can be multiple connection strings separate by ;
// e.g. 127.0.0.1:9090
func (rp *MemProvider) SessionInit(maxlifetime int64, savePath string) error {
rp.maxlifetime = maxlifetime
rp.conninfo = strings.Split(savePath, ";")
client = memcache.New(rp.conninfo...)
func (p *MemProvider) Init(maxlifetime int64, connStrs string) error {
p.maxlifetime = maxlifetime
p.conninfo = strings.Split(connStrs, ";")
client = memcache.New(p.conninfo...)
return nil
}
func (rp *MemProvider) connectInit() error {
client = memcache.New(rp.conninfo...)
func (p *MemProvider) connectInit() error {
client = memcache.New(p.conninfo...)
return nil
}
// read redis session by sid
func (rp *MemProvider) SessionRead(sid string) (session.RawStore, error) {
// Read returns raw session store by session ID.
func (p *MemProvider) Read(sid string) (session.RawStore, error) {
if client == nil {
if err := rp.connectInit(); err != nil {
if err := p.connectInit(); err != nil {
return nil, err
}
}
@ -138,14 +136,14 @@ func (rp *MemProvider) SessionRead(sid string) (session.RawStore, error) {
}
}
rs := &MemcacheSessionStore{sid: sid, values: kv, maxlifetime: rp.maxlifetime}
rs := &MemcacheSessionStore{sid: sid, data: kv, maxlifetime: p.maxlifetime}
return rs, nil
}
// check redis session exist by sid
func (rp *MemProvider) SessionExist(sid string) bool {
// Exist returns true if session with given ID exists.
func (p *MemProvider) Exist(sid string) bool {
if client == nil {
if err := rp.connectInit(); err != nil {
if err := p.connectInit(); err != nil {
return false
}
}
@ -157,10 +155,21 @@ func (rp *MemProvider) SessionExist(sid string) bool {
}
}
// generate new sid for redis session
func (rp *MemProvider) SessionRegenerate(oldsid, sid string) (session.RawStore, error) {
// Destory deletes a session by session ID.
func (p *MemProvider) Destory(sid string) error {
if client == nil {
if err := rp.connectInit(); err != nil {
if err := p.connectInit(); err != nil {
return err
}
}
return client.Delete(sid)
}
// Regenerate regenerates a session store from old session ID to new one.
func (p *MemProvider) Regenerate(oldsid, sid string) (session.RawStore, error) {
if client == nil {
if err := p.connectInit(); err != nil {
return nil, err
}
}
@ -172,13 +181,13 @@ func (rp *MemProvider) SessionRegenerate(oldsid, sid string) (session.RawStore,
// the existed value will be 0
item.Key = sid
item.Value = []byte("")
item.Expiration = int32(rp.maxlifetime)
item.Expiration = int32(p.maxlifetime)
client.Set(item)
} else {
client.Delete(oldsid)
item.Key = sid
item.Value = item.Value
item.Expiration = int32(rp.maxlifetime)
item.Expiration = int32(p.maxlifetime)
client.Set(item)
contain = item.Value
}
@ -194,31 +203,19 @@ func (rp *MemProvider) SessionRegenerate(oldsid, sid string) (session.RawStore,
}
}
rs := &MemcacheSessionStore{sid: sid, values: kv, maxlifetime: rp.maxlifetime}
rs := &MemcacheSessionStore{sid: sid, data: kv, maxlifetime: p.maxlifetime}
return rs, nil
}
// delete redis session by id
func (rp *MemProvider) SessionDestroy(sid string) error {
if client == nil {
if err := rp.connectInit(); err != nil {
return err
}
}
return client.Delete(sid)
}
// Impelment method, no used.
func (rp *MemProvider) SessionGC() {
return
}
// @todo
func (rp *MemProvider) SessionAll() int {
// Count counts and returns number of sessions.
func (p *MemProvider) Count() int {
// FIXME
return 0
}
// GC calls GC to clean expired sessions.
func (p *MemProvider) GC() {}
func init() {
session.Register("memcache", mempder)
session.Register("memcache", &MemProvider{})
}

View File

@ -15,17 +15,8 @@
package session
// mysql session support need create table as sql:
// CREATE TABLE `session` (
// `session_key` char(64) NOT NULL,
// session_data` blob,
// `session_expiry` int(11) unsigned NOT NULL,
// PRIMARY KEY (`session_key`)
// ) ENGINE=MyISAM DEFAULT CHARSET=utf8;
import (
"database/sql"
"net/http"
"sync"
"time"
@ -34,96 +25,90 @@ import (
"github.com/macaron-contrib/session"
)
var mysqlpder = &MysqlProvider{}
// mysql session store
// MysqlSessionStore represents a mysql session store implementation.
type MysqlSessionStore struct {
c *sql.DB
sid string
lock sync.RWMutex
values map[interface{}]interface{}
c *sql.DB
sid string
lock sync.RWMutex
data map[interface{}]interface{}
}
// set value in mysql session.
// it is temp value in map.
func (st *MysqlSessionStore) Set(key, value interface{}) error {
st.lock.Lock()
defer st.lock.Unlock()
st.values[key] = value
// Set sets value to given key in session.
func (s *MysqlSessionStore) Set(key, val interface{}) error {
s.lock.Lock()
defer s.lock.Unlock()
s.data[key] = val
return nil
}
// get value from mysql session
func (st *MysqlSessionStore) Get(key interface{}) interface{} {
st.lock.RLock()
defer st.lock.RUnlock()
if v, ok := st.values[key]; ok {
return v
} else {
return nil
}
// Get gets value by given key in session.
func (s *MysqlSessionStore) Get(key interface{}) interface{} {
s.lock.RLock()
defer s.lock.RUnlock()
return s.data[key]
}
// delete value in mysql session
func (st *MysqlSessionStore) Delete(key interface{}) error {
st.lock.Lock()
defer st.lock.Unlock()
delete(st.values, key)
// Delete delete a key from session.
func (s *MysqlSessionStore) Delete(key interface{}) error {
s.lock.Lock()
defer s.lock.Unlock()
delete(s.data, key)
return nil
}
// clear all values in mysql session
func (st *MysqlSessionStore) Flush() error {
st.lock.Lock()
defer st.lock.Unlock()
st.values = make(map[interface{}]interface{})
return nil
// ID returns current session ID.
func (s *MysqlSessionStore) ID() string {
return s.sid
}
// get session id of this mysql session store
func (st *MysqlSessionStore) SessionID() string {
return st.sid
}
// save mysql session values to database.
// must call this method to save values to database.
func (st *MysqlSessionStore) SessionRelease(w http.ResponseWriter) {
defer st.c.Close()
b, err := session.EncodeGob(st.values)
// Release releases resource and save data to provider.
func (s *MysqlSessionStore) Release() error {
defer s.c.Close()
data, err := session.EncodeGob(s.data)
if err != nil {
return
return err
}
st.c.Exec("UPDATE session set `session_data`=?, `session_expiry`=? where session_key=?",
b, time.Now().Unix(), st.sid)
_, err = s.c.Exec("UPDATE session set `session_data`=?, `session_expiry`=? where session_key=?",
data, time.Now().Unix(), s.sid)
return err
}
// mysql session provider
// Flush deletes all session data.
func (s *MysqlSessionStore) Flush() error {
s.lock.Lock()
defer s.lock.Unlock()
s.data = make(map[interface{}]interface{})
return nil
}
// MysqlProvider represents a mysql session provider implementation.
type MysqlProvider struct {
maxlifetime int64
savePath string
connStr string
}
// connect to mysql
func (mp *MysqlProvider) connectInit() *sql.DB {
db, e := sql.Open("mysql", mp.savePath)
func (p *MysqlProvider) connectInit() *sql.DB {
db, e := sql.Open("mysql", p.connStr)
if e != nil {
return nil
}
return db
}
// init mysql session.
// savepath is the connection string of mysql.
func (mp *MysqlProvider) SessionInit(maxlifetime int64, savePath string) error {
mp.maxlifetime = maxlifetime
mp.savePath = savePath
// Init initializes memory session provider.
func (p *MysqlProvider) Init(maxlifetime int64, connStr string) error {
p.maxlifetime = maxlifetime
p.connStr = connStr
return nil
}
// get mysql session by sid
func (mp *MysqlProvider) SessionRead(sid string) (session.RawStore, error) {
c := mp.connectInit()
// Read returns raw session store by session ID.
func (p *MysqlProvider) Read(sid string) (session.RawStore, error) {
c := p.connectInit()
row := c.QueryRow("select session_data from session where session_key=?", sid)
var sessiondata []byte
err := row.Scan(&sessiondata)
@ -140,14 +125,15 @@ func (mp *MysqlProvider) SessionRead(sid string) (session.RawStore, error) {
return nil, err
}
}
rs := &MysqlSessionStore{c: c, sid: sid, values: kv}
rs := &MysqlSessionStore{c: c, sid: sid, data: kv}
return rs, nil
}
// check mysql session exist
func (mp *MysqlProvider) SessionExist(sid string) bool {
c := mp.connectInit()
// Exist returns true if session with given ID exists.
func (p *MysqlProvider) Exist(sid string) bool {
c := p.connectInit()
defer c.Close()
row := c.QueryRow("select session_data from session where session_key=?", sid)
var sessiondata []byte
err := row.Scan(&sessiondata)
@ -158,9 +144,18 @@ func (mp *MysqlProvider) SessionExist(sid string) bool {
}
}
// generate new sid for mysql session
func (mp *MysqlProvider) SessionRegenerate(oldsid, sid string) (session.RawStore, error) {
c := mp.connectInit()
// Destory deletes a session by session ID.
func (p *MysqlProvider) Destory(sid string) (err error) {
c := p.connectInit()
if _, err = c.Exec("DELETE FROM session where session_key=?", sid); err != nil {
return err
}
return c.Close()
}
// Regenerate regenerates a session store from old session ID to new one.
func (p *MysqlProvider) Regenerate(oldsid, sid string) (session.RawStore, error) {
c := p.connectInit()
row := c.QueryRow("select session_data from session where session_key=?", oldsid)
var sessiondata []byte
err := row.Scan(&sessiondata)
@ -177,30 +172,15 @@ func (mp *MysqlProvider) SessionRegenerate(oldsid, sid string) (session.RawStore
return nil, err
}
}
rs := &MysqlSessionStore{c: c, sid: sid, values: kv}
rs := &MysqlSessionStore{c: c, sid: sid, data: kv}
return rs, nil
}
// delete mysql session by sid
func (mp *MysqlProvider) SessionDestroy(sid string) error {
c := mp.connectInit()
c.Exec("DELETE FROM session where session_key=?", sid)
c.Close()
return nil
}
// delete expired values in mysql session
func (mp *MysqlProvider) SessionGC() {
c := mp.connectInit()
c.Exec("DELETE from session where session_expiry < ?", time.Now().Unix()-mp.maxlifetime)
c.Close()
return
}
// count values in mysql session
func (mp *MysqlProvider) SessionAll() int {
c := mp.connectInit()
// Count counts and returns number of sessions.
func (p *MysqlProvider) Count() int {
c := p.connectInit()
defer c.Close()
var total int
err := c.QueryRow("SELECT count(*) as num from session").Scan(&total)
if err != nil {
@ -209,6 +189,13 @@ func (mp *MysqlProvider) SessionAll() int {
return total
}
func init() {
session.Register("mysql", mysqlpder)
// GC calls GC to clean expired sessions.
func (mp *MysqlProvider) GC() {
c := mp.connectInit()
c.Exec("DELETE from session where session_expiry < ?", time.Now().Unix()-mp.maxlifetime)
c.Close()
}
func init() {
session.Register("mysql", &MysqlProvider{})
}

View File

@ -15,38 +15,8 @@
package session
/*
beego session provider for postgresql
-------------------------------------
depends on github.com/lib/pq:
go install github.com/lib/pq
needs this table in your database:
CREATE TABLE session (
session_key char(64) NOT NULL,
session_data bytea,
session_expiry timestamp NOT NULL,
CONSTRAINT session_key PRIMARY KEY(session_key)
);
will be activated with these settings in app.conf:
SessionOn = true
SessionProvider = postgresql
SessionSavePath = "user=a password=b dbname=c sslmode=disable"
SessionName = session
*/
import (
"database/sql"
"net/http"
"sync"
"time"
@ -55,96 +25,93 @@ import (
"github.com/macaron-contrib/session"
)
var postgresqlpder = &PostgresqlProvider{}
// postgresql session store
// PostgresqlSessionStore represents a postgresql session store implementation.
type PostgresqlSessionStore struct {
c *sql.DB
sid string
lock sync.RWMutex
values map[interface{}]interface{}
c *sql.DB
sid string
lock sync.RWMutex
data map[interface{}]interface{}
}
// set value in postgresql session.
// it is temp value in map.
func (st *PostgresqlSessionStore) Set(key, value interface{}) error {
st.lock.Lock()
defer st.lock.Unlock()
st.values[key] = value
// Set sets value to given key in session.
func (s *PostgresqlSessionStore) Set(key, value interface{}) error {
s.lock.Lock()
defer s.lock.Unlock()
s.data[key] = value
return nil
}
// get value from postgresql session
func (st *PostgresqlSessionStore) Get(key interface{}) interface{} {
st.lock.RLock()
defer st.lock.RUnlock()
if v, ok := st.values[key]; ok {
return v
} else {
return nil
}
// Get gets value by given key in session.
func (s *PostgresqlSessionStore) Get(key interface{}) interface{} {
s.lock.RLock()
defer s.lock.RUnlock()
return s.data[key]
}
// delete value in postgresql session
func (st *PostgresqlSessionStore) Delete(key interface{}) error {
st.lock.Lock()
defer st.lock.Unlock()
delete(st.values, key)
// Delete delete a key from session.
func (s *PostgresqlSessionStore) Delete(key interface{}) error {
s.lock.Lock()
defer s.lock.Unlock()
delete(s.data, key)
return nil
}
// clear all values in postgresql session
func (st *PostgresqlSessionStore) Flush() error {
st.lock.Lock()
defer st.lock.Unlock()
st.values = make(map[interface{}]interface{})
return nil
}
// get session id of this postgresql session store
func (st *PostgresqlSessionStore) SessionID() string {
return st.sid
// ID returns current session ID.
func (s *PostgresqlSessionStore) ID() string {
return s.sid
}
// save postgresql session values to database.
// must call this method to save values to database.
func (st *PostgresqlSessionStore) SessionRelease(w http.ResponseWriter) {
defer st.c.Close()
b, err := session.EncodeGob(st.values)
if err != nil {
return
}
st.c.Exec("UPDATE session set session_data=$1, session_expiry=$2 where session_key=$3",
b, time.Now().Format(time.RFC3339), st.sid)
func (s *PostgresqlSessionStore) Release() error {
defer s.c.Close()
data, err := session.EncodeGob(s.data)
if err != nil {
return err
}
_, err = s.c.Exec("UPDATE session set session_data=$1, session_expiry=$2 where session_key=$3",
data, time.Now().Format(time.RFC3339), s.sid)
return err
}
// postgresql session provider
// Flush deletes all session data.
func (s *PostgresqlSessionStore) Flush() error {
s.lock.Lock()
defer s.lock.Unlock()
s.data = make(map[interface{}]interface{})
return nil
}
// PostgresqlProvider represents a postgresql session provider implementation.
type PostgresqlProvider struct {
maxlifetime int64
savePath string
connStr string
}
// connect to postgresql
func (mp *PostgresqlProvider) connectInit() *sql.DB {
db, e := sql.Open("postgres", mp.savePath)
func (p *PostgresqlProvider) connectInit() *sql.DB {
db, e := sql.Open("postgres", p.connStr)
if e != nil {
return nil
}
return db
}
// init postgresql session.
// savepath is the connection string of postgresql.
func (mp *PostgresqlProvider) SessionInit(maxlifetime int64, savePath string) error {
mp.maxlifetime = maxlifetime
mp.savePath = savePath
// Init initializes memory session provider.
func (p *PostgresqlProvider) Init(maxlifetime int64, connStr string) error {
p.maxlifetime = maxlifetime
p.connStr = connStr
return nil
}
// get postgresql session by sid
func (mp *PostgresqlProvider) SessionRead(sid string) (session.RawStore, error) {
c := mp.connectInit()
// Read returns raw session store by session ID.
func (p *PostgresqlProvider) Read(sid string) (session.RawStore, error) {
c := p.connectInit()
row := c.QueryRow("select session_data from session where session_key=$1", sid)
var sessiondata []byte
err := row.Scan(&sessiondata)
@ -168,13 +135,13 @@ func (mp *PostgresqlProvider) SessionRead(sid string) (session.RawStore, error)
return nil, err
}
}
rs := &PostgresqlSessionStore{c: c, sid: sid, values: kv}
rs := &PostgresqlSessionStore{c: c, sid: sid, data: kv}
return rs, nil
}
// check postgresql session exist
func (mp *PostgresqlProvider) SessionExist(sid string) bool {
c := mp.connectInit()
// Exist returns true if session with given ID exists.
func (p *PostgresqlProvider) Exist(sid string) bool {
c := p.connectInit()
defer c.Close()
row := c.QueryRow("select session_data from session where session_key=$1", sid)
var sessiondata []byte
@ -187,9 +154,18 @@ func (mp *PostgresqlProvider) SessionExist(sid string) bool {
}
}
// generate new sid for postgresql session
func (mp *PostgresqlProvider) SessionRegenerate(oldsid, sid string) (session.RawStore, error) {
c := mp.connectInit()
// Destory deletes a session by session ID.
func (p *PostgresqlProvider) Destory(sid string) (err error) {
c := p.connectInit()
if _, err = c.Exec("DELETE FROM session where session_key=$1", sid); err != nil {
return err
}
return c.Close()
}
// Regenerate regenerates a session store from old session ID to new one.
func (p *PostgresqlProvider) Regenerate(oldsid, sid string) (session.RawStore, error) {
c := p.connectInit()
row := c.QueryRow("select session_data from session where session_key=$1", oldsid)
var sessiondata []byte
err := row.Scan(&sessiondata)
@ -207,29 +183,13 @@ func (mp *PostgresqlProvider) SessionRegenerate(oldsid, sid string) (session.Raw
return nil, err
}
}
rs := &PostgresqlSessionStore{c: c, sid: sid, values: kv}
rs := &PostgresqlSessionStore{c: c, sid: sid, data: kv}
return rs, nil
}
// delete postgresql session by sid
func (mp *PostgresqlProvider) SessionDestroy(sid string) error {
c := mp.connectInit()
c.Exec("DELETE FROM session where session_key=$1", sid)
c.Close()
return nil
}
// delete expired values in postgresql session
func (mp *PostgresqlProvider) SessionGC() {
c := mp.connectInit()
c.Exec("DELETE from session where EXTRACT(EPOCH FROM (current_timestamp - session_expiry)) > $1", mp.maxlifetime)
c.Close()
return
}
// count values in postgresql session
func (mp *PostgresqlProvider) SessionAll() int {
c := mp.connectInit()
// Count counts and returns number of sessions.
func (p *PostgresqlProvider) Count() int {
c := p.connectInit()
defer c.Close()
var total int
err := c.QueryRow("SELECT count(*) as num from session").Scan(&total)
@ -239,6 +199,13 @@ func (mp *PostgresqlProvider) SessionAll() int {
return total
}
func init() {
session.Register("postgresql", postgresqlpder)
// GC calls GC to clean expired sessions.
func (mp *PostgresqlProvider) GC() {
c := mp.connectInit()
c.Exec("DELETE from session where EXTRACT(EPOCH FROM (current_timestamp - session_expiry)) > $1", mp.maxlifetime)
c.Close()
}
func init() {
session.Register("postgresql", &PostgresqlProvider{})
}

View File

@ -16,7 +16,6 @@
package session
import (
"net/http"
"strconv"
"strings"
"sync"
@ -26,126 +25,125 @@ import (
"github.com/macaron-contrib/session"
)
var redispder = &RedisProvider{}
// redis max pool size
var MAX_POOL_SIZE = 100
var redisPool chan redis.Conn
// redis session store
// RedisSessionStore represents a redis session store implementation.
type RedisSessionStore struct {
p *redis.Pool
sid string
lock sync.RWMutex
values map[interface{}]interface{}
data map[interface{}]interface{}
maxlifetime int64
}
// set value in redis session
func (rs *RedisSessionStore) Set(key, value interface{}) error {
rs.lock.Lock()
defer rs.lock.Unlock()
rs.values[key] = value
// Set sets value to given key in session.
func (s *RedisSessionStore) Set(key, val interface{}) error {
s.lock.Lock()
defer s.lock.Unlock()
s.data[key] = val
return nil
}
// get value in redis session
func (rs *RedisSessionStore) Get(key interface{}) interface{} {
rs.lock.RLock()
defer rs.lock.RUnlock()
if v, ok := rs.values[key]; ok {
return v
} else {
return nil
}
// Get gets value by given key in session.
func (s *RedisSessionStore) Get(key interface{}) interface{} {
s.lock.RLock()
defer s.lock.RUnlock()
return s.data[key]
}
// delete value in redis session
func (rs *RedisSessionStore) Delete(key interface{}) error {
rs.lock.Lock()
defer rs.lock.Unlock()
delete(rs.values, key)
// Delete delete a key from session.
func (s *RedisSessionStore) Delete(key interface{}) error {
s.lock.Lock()
defer s.lock.Unlock()
delete(s.data, key)
return nil
}
// clear all values in redis session
func (rs *RedisSessionStore) Flush() error {
rs.lock.Lock()
defer rs.lock.Unlock()
rs.values = make(map[interface{}]interface{})
return nil
// ID returns current session ID.
func (s *RedisSessionStore) ID() string {
return s.sid
}
// get redis session id
func (rs *RedisSessionStore) SessionID() string {
return rs.sid
}
// save session values to redis
func (rs *RedisSessionStore) SessionRelease(w http.ResponseWriter) {
c := rs.p.Get()
// Release releases resource and save data to provider.
func (s *RedisSessionStore) Release() error {
c := s.p.Get()
defer c.Close()
b, err := session.EncodeGob(rs.values)
data, err := session.EncodeGob(s.data)
if err != nil {
return
return err
}
c.Do("SETEX", rs.sid, rs.maxlifetime, string(b))
_, err = c.Do("SETEX", s.sid, s.maxlifetime, string(data))
return err
}
// redis session provider
// Flush deletes all session data.
func (s *RedisSessionStore) Flush() error {
s.lock.Lock()
defer s.lock.Unlock()
s.data = make(map[interface{}]interface{})
return nil
}
// RedisProvider represents a redis session provider implementation.
type RedisProvider struct {
maxlifetime int64
savePath string
connAddr string
poolsize int
password string
poollist *redis.Pool
}
// init redis session
// savepath like redis server addr,pool size,password
// e.g. 127.0.0.1:6379,100,astaxie
func (rp *RedisProvider) SessionInit(maxlifetime int64, savePath string) error {
rp.maxlifetime = maxlifetime
configs := strings.Split(savePath, ",")
// Init initializes memory session provider.
// connStr: <redis server addr>,<pool size>,<password>
// e.g. 127.0.0.1:6379,100,macaron
func (p *RedisProvider) Init(maxlifetime int64, connStr string) error {
p.maxlifetime = maxlifetime
configs := strings.Split(connStr, ",")
if len(configs) > 0 {
rp.savePath = configs[0]
p.connAddr = configs[0]
}
if len(configs) > 1 {
poolsize, err := strconv.Atoi(configs[1])
if err != nil || poolsize <= 0 {
rp.poolsize = MAX_POOL_SIZE
p.poolsize = MAX_POOL_SIZE
} else {
rp.poolsize = poolsize
p.poolsize = poolsize
}
} else {
rp.poolsize = MAX_POOL_SIZE
p.poolsize = MAX_POOL_SIZE
}
if len(configs) > 2 {
rp.password = configs[2]
p.password = configs[2]
}
rp.poollist = redis.NewPool(func() (redis.Conn, error) {
c, err := redis.Dial("tcp", rp.savePath)
p.poollist = redis.NewPool(func() (redis.Conn, error) {
c, err := redis.Dial("tcp", p.connAddr)
if err != nil {
return nil, err
}
if rp.password != "" {
if _, err := c.Do("AUTH", rp.password); err != nil {
if p.password != "" {
if _, err := c.Do("AUTH", p.password); err != nil {
c.Close()
return nil, err
}
}
return c, err
}, rp.poolsize)
}, p.poolsize)
return rp.poollist.Get().Err()
return p.poollist.Get().Err()
}
// read redis session by sid
func (rp *RedisProvider) SessionRead(sid string) (session.RawStore, error) {
c := rp.poollist.Get()
// Read returns raw session store by session ID.
func (p *RedisProvider) Read(sid string) (session.RawStore, error) {
c := p.poollist.Get()
defer c.Close()
kvs, err := redis.String(c.Do("GET", sid))
@ -159,13 +157,13 @@ func (rp *RedisProvider) SessionRead(sid string) (session.RawStore, error) {
}
}
rs := &RedisSessionStore{p: rp.poollist, sid: sid, values: kv, maxlifetime: rp.maxlifetime}
rs := &RedisSessionStore{p: p.poollist, sid: sid, data: kv, maxlifetime: p.maxlifetime}
return rs, nil
}
// check redis session exist by sid
func (rp *RedisProvider) SessionExist(sid string) bool {
c := rp.poollist.Get()
// Exist returns true if session with given ID exists.
func (p *RedisProvider) Exist(sid string) bool {
c := p.poollist.Get()
defer c.Close()
if existed, err := redis.Int(c.Do("EXISTS", sid)); err != nil || existed == 0 {
@ -175,19 +173,28 @@ func (rp *RedisProvider) SessionExist(sid string) bool {
}
}
// generate new sid for redis session
func (rp *RedisProvider) SessionRegenerate(oldsid, sid string) (session.RawStore, error) {
c := rp.poollist.Get()
// Destory deletes a session by session ID.
func (p *RedisProvider) Destory(sid string) error {
c := p.poollist.Get()
defer c.Close()
_, err := c.Do("DEL", sid)
return err
}
// Regenerate regenerates a session store from old session ID to new one.
func (p *RedisProvider) Regenerate(oldsid, sid string) (session.RawStore, error) {
c := p.poollist.Get()
defer c.Close()
if existed, _ := redis.Int(c.Do("EXISTS", oldsid)); existed == 0 {
// oldsid doesn't exists, set the new sid directly
// ignore error here, since if it return error
// the existed value will be 0
c.Do("SET", sid, "", "EX", rp.maxlifetime)
c.Do("SET", sid, "", "EX", p.maxlifetime)
} else {
c.Do("RENAME", oldsid, sid)
c.Do("EXPIRE", sid, rp.maxlifetime)
c.Do("EXPIRE", sid, p.maxlifetime)
}
kvs, err := redis.String(c.Do("GET", sid))
@ -201,29 +208,19 @@ func (rp *RedisProvider) SessionRegenerate(oldsid, sid string) (session.RawStore
}
}
rs := &RedisSessionStore{p: rp.poollist, sid: sid, values: kv, maxlifetime: rp.maxlifetime}
rs := &RedisSessionStore{p: p.poollist, sid: sid, data: kv, maxlifetime: p.maxlifetime}
return rs, nil
}
// delete redis session by id
func (rp *RedisProvider) SessionDestroy(sid string) error {
c := rp.poollist.Get()
defer c.Close()
c.Do("DEL", sid)
return nil
}
// Impelment method, no used.
func (rp *RedisProvider) SessionGC() {
return
}
// @todo
func (rp *RedisProvider) SessionAll() int {
// Count counts and returns number of sessions.
func (p *RedisProvider) Count() int {
// FIXME
return 0
}
// GC calls GC to clean expired sessions.
func (_ *RedisProvider) GC() {}
func init() {
session.Register("redis", redispder)
session.Register("redis", &RedisProvider{})
}

View File

@ -1,186 +0,0 @@
// Copyright 2013 Beego Authors
//
// Licensed under the Apache License, Version 2.0 (the "License"): you may
// not use this file except in compliance with the License. You may obtain
// a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
// License for the specific language governing permissions and limitations
// under the License.
package session
import (
"crypto/aes"
"crypto/cipher"
"encoding/json"
"net/http"
"net/url"
"sync"
)
var cookiepder = &CookieProvider{}
// Cookie SessionStore
type CookieSessionStore struct {
sid string
values map[interface{}]interface{} // session data
lock sync.RWMutex
}
// Set value to cookie session.
// the value are encoded as gob with hash block string.
func (st *CookieSessionStore) Set(key, value interface{}) error {
st.lock.Lock()
defer st.lock.Unlock()
st.values[key] = value
return nil
}
// Get value from cookie session
func (st *CookieSessionStore) Get(key interface{}) interface{} {
st.lock.RLock()
defer st.lock.RUnlock()
if v, ok := st.values[key]; ok {
return v
} else {
return nil
}
}
// Delete value in cookie session
func (st *CookieSessionStore) Delete(key interface{}) error {
st.lock.Lock()
defer st.lock.Unlock()
delete(st.values, key)
return nil
}
// Clean all values in cookie session
func (st *CookieSessionStore) Flush() error {
st.lock.Lock()
defer st.lock.Unlock()
st.values = make(map[interface{}]interface{})
return nil
}
// Return id of this cookie session
func (st *CookieSessionStore) SessionID() string {
return st.sid
}
// Write cookie session to http response cookie
func (st *CookieSessionStore) SessionRelease(w http.ResponseWriter) {
str, err := encodeCookie(cookiepder.block,
cookiepder.config.SecurityKey,
cookiepder.config.SecurityName,
st.values)
if err != nil {
return
}
cookie := &http.Cookie{Name: cookiepder.config.CookieName,
Value: url.QueryEscape(str),
Path: cookiepder.config.CookiePath,
HttpOnly: true,
Secure: cookiepder.config.Secure,
MaxAge: cookiepder.config.Maxage}
http.SetCookie(w, cookie)
return
}
type cookieConfig struct {
SecurityKey string `json:"securityKey"`
BlockKey string `json:"blockKey"`
SecurityName string `json:"securityName"`
CookieName string `json:"cookieName"`
CookiePath string `json:"cookiePath"`
Secure bool `json:"secure"`
Maxage int `json:"maxage"`
}
// Cookie session provider
type CookieProvider struct {
maxlifetime int64
config *cookieConfig
block cipher.Block
}
// Init cookie session provider with max lifetime and config json.
// maxlifetime is ignored.
// json config:
// securityKey - hash string
// blockKey - gob encode hash string. it's saved as aes crypto.
// securityName - recognized name in encoded cookie string
// cookieName - cookie name
// maxage - cookie max life time.
func (pder *CookieProvider) SessionInit(maxlifetime int64, config string) error {
pder.config = &cookieConfig{}
err := json.Unmarshal([]byte(config), pder.config)
if err != nil {
return err
}
if pder.config.BlockKey == "" {
pder.config.BlockKey = string(generateRandomKey(16))
}
if pder.config.SecurityName == "" {
pder.config.SecurityName = string(generateRandomKey(20))
}
pder.block, err = aes.NewCipher([]byte(pder.config.BlockKey))
if err != nil {
return err
}
pder.maxlifetime = maxlifetime
return nil
}
// Get SessionStore in cooke.
// decode cooke string to map and put into SessionStore with sid.
func (pder *CookieProvider) SessionRead(sid string) (RawStore, error) {
maps, _ := decodeCookie(pder.block,
pder.config.SecurityKey,
pder.config.SecurityName,
sid, pder.maxlifetime)
if maps == nil {
maps = make(map[interface{}]interface{})
}
rs := &CookieSessionStore{sid: sid, values: maps}
return rs, nil
}
// Cookie session is always existed
func (pder *CookieProvider) SessionExist(sid string) bool {
return true
}
// Implement method, no used.
func (pder *CookieProvider) SessionRegenerate(oldsid, sid string) (RawStore, error) {
return nil, nil
}
// Implement method, no used.
func (pder *CookieProvider) SessionDestroy(sid string) error {
return nil
}
// Implement method, no used.
func (pder *CookieProvider) SessionGC() {
return
}
// Implement method, return 0.
func (pder *CookieProvider) SessionAll() int {
return 0
}
// Implement method, no used.
func (pder *CookieProvider) SessionUpdate(sid string) error {
return nil
}
func init() {
Register("cookie", cookiepder)
}

View File

@ -1,60 +0,0 @@
// Copyright 2013 Beego Authors
// Copyright 2014 Unknwon
//
// Licensed under the Apache License, Version 2.0 (the "License"): you may
// not use this file except in compliance with the License. You may obtain
// a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
// License for the specific language governing permissions and limitations
// under the License.
package session
import (
"net/http"
"net/http/httptest"
"strings"
"testing"
)
func TestCookie(t *testing.T) {
config := &Config{
CookieName: "gosessionid",
Gclifetime: 3600,
ProviderConfig: "{\"cookieName\":\"gosessionid\",\"securityKey\":\"macaroncookiehashkey\"}",
}
globalSessions, err := NewManager("cookie", config)
if err != nil {
t.Fatal("init cookie session err", err)
}
r, _ := http.NewRequest("GET", "/", nil)
w := httptest.NewRecorder()
sess, err := globalSessions.SessionStart(w, r)
if err != nil {
t.Fatal("start session,", err)
}
err = sess.Set("username", "astaxie")
if err != nil {
t.Fatal("set error,", err)
}
if username := sess.Get("username"); username != "astaxie" {
t.Fatal("get username error")
}
sess.SessionRelease(w)
if cookiestr := w.Header().Get("Set-Cookie"); cookiestr == "" {
t.Fatal("setcookie error")
} else {
parts := strings.Split(strings.TrimSpace(cookiestr), ";")
for k, v := range parts {
nameval := strings.Split(v, "=")
if k == 0 && nameval[0] != "gosessionid" {
t.Fatal("error")
}
}
}
}

View File

@ -1,288 +0,0 @@
// Copyright 2013 Beego Authors
//
// Licensed under the Apache License, Version 2.0 (the "License"): you may
// not use this file except in compliance with the License. You may obtain
// a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
// License for the specific language governing permissions and limitations
// under the License.
package session
import (
"errors"
"fmt"
"io"
"io/ioutil"
"net/http"
"os"
"path"
"path/filepath"
"sync"
"time"
)
var (
filepder = &FileProvider{}
gcmaxlifetime int64
)
// File session store
type FileSessionStore struct {
f *os.File
sid string
lock sync.RWMutex
values map[interface{}]interface{}
}
// Set value to file session
func (fs *FileSessionStore) Set(key, value interface{}) error {
fs.lock.Lock()
defer fs.lock.Unlock()
fs.values[key] = value
return nil
}
// Get value from file session
func (fs *FileSessionStore) Get(key interface{}) interface{} {
fs.lock.RLock()
defer fs.lock.RUnlock()
if v, ok := fs.values[key]; ok {
return v
} else {
return nil
}
}
// Delete value in file session by given key
func (fs *FileSessionStore) Delete(key interface{}) error {
fs.lock.Lock()
defer fs.lock.Unlock()
delete(fs.values, key)
return nil
}
// Clean all values in file session
func (fs *FileSessionStore) Flush() error {
fs.lock.Lock()
defer fs.lock.Unlock()
fs.values = make(map[interface{}]interface{})
return nil
}
// Get file session store id
func (fs *FileSessionStore) SessionID() string {
return fs.sid
}
// Write file session to local file with Gob string
func (fs *FileSessionStore) SessionRelease(w http.ResponseWriter) {
b, err := EncodeGob(fs.values)
if err != nil {
return
}
_, err = os.Stat(path.Join(filepder.savePath, string(fs.sid[0]), string(fs.sid[1]), fs.sid))
var f *os.File
if err == nil {
f, err = os.OpenFile(path.Join(filepder.savePath, string(fs.sid[0]), string(fs.sid[1]), fs.sid), os.O_RDWR, 0777)
} else if os.IsNotExist(err) {
f, err = os.Create(path.Join(filepder.savePath, string(fs.sid[0]), string(fs.sid[1]), fs.sid))
} else {
return
}
f.Truncate(0)
f.Seek(0, 0)
f.Write(b)
f.Close()
}
// File session provider
type FileProvider struct {
lock sync.RWMutex
maxlifetime int64
savePath string
}
// Init file session provider.
// savePath sets the session files path.
func (fp *FileProvider) SessionInit(maxlifetime int64, savePath string) error {
fp.maxlifetime = maxlifetime
fp.savePath = savePath
return nil
}
// Read file session by sid.
// if file is not exist, create it.
// the file path is generated from sid string.
func (fp *FileProvider) SessionRead(sid string) (RawStore, error) {
filepder.lock.Lock()
defer filepder.lock.Unlock()
err := os.MkdirAll(path.Join(fp.savePath, string(sid[0]), string(sid[1])), 0777)
if err != nil {
println(err.Error())
}
_, err = os.Stat(path.Join(fp.savePath, string(sid[0]), string(sid[1]), sid))
var f *os.File
if err == nil {
f, err = os.OpenFile(path.Join(fp.savePath, string(sid[0]), string(sid[1]), sid), os.O_RDWR, 0777)
} else if os.IsNotExist(err) {
f, err = os.Create(path.Join(fp.savePath, string(sid[0]), string(sid[1]), sid))
} else {
return nil, err
}
os.Chtimes(path.Join(fp.savePath, string(sid[0]), string(sid[1]), sid), time.Now(), time.Now())
var kv map[interface{}]interface{}
b, err := ioutil.ReadAll(f)
if err != nil {
return nil, err
}
if len(b) == 0 {
kv = make(map[interface{}]interface{})
} else {
kv, err = DecodeGob(b)
if err != nil {
return nil, err
}
}
f.Close()
ss := &FileSessionStore{sid: sid, values: kv}
return ss, nil
}
// Check file session exist.
// it checkes the file named from sid exist or not.
func (fp *FileProvider) SessionExist(sid string) bool {
filepder.lock.Lock()
defer filepder.lock.Unlock()
_, err := os.Stat(path.Join(fp.savePath, string(sid[0]), string(sid[1]), sid))
if err == nil {
return true
} else {
return false
}
}
// Remove all files in this save path
func (fp *FileProvider) SessionDestroy(sid string) error {
filepder.lock.Lock()
defer filepder.lock.Unlock()
os.Remove(path.Join(fp.savePath, string(sid[0]), string(sid[1]), sid))
return nil
}
// Recycle files in save path
func (fp *FileProvider) SessionGC() {
filepder.lock.Lock()
defer filepder.lock.Unlock()
gcmaxlifetime = fp.maxlifetime
filepath.Walk(fp.savePath, gcpath)
}
// Get active file session number.
// it walks save path to count files.
func (fp *FileProvider) SessionAll() int {
a := &activeSession{}
err := filepath.Walk(fp.savePath, func(path string, f os.FileInfo, err error) error {
return a.visit(path, f, err)
})
if err != nil {
fmt.Printf("filepath.Walk() returned %v\n", err)
return 0
}
return a.total
}
// Generate new sid for file session.
// it delete old file and create new file named from new sid.
func (fp *FileProvider) SessionRegenerate(oldsid, sid string) (RawStore, error) {
filepder.lock.Lock()
defer filepder.lock.Unlock()
err := os.MkdirAll(path.Join(fp.savePath, string(oldsid[0]), string(oldsid[1])), 0777)
if err != nil {
println(err.Error())
}
err = os.MkdirAll(path.Join(fp.savePath, string(sid[0]), string(sid[1])), 0777)
if err != nil {
println(err.Error())
}
_, err = os.Stat(path.Join(fp.savePath, string(sid[0]), string(sid[1]), sid))
var newf *os.File
if err == nil {
return nil, errors.New("newsid exist")
} else if os.IsNotExist(err) {
newf, err = os.Create(path.Join(fp.savePath, string(sid[0]), string(sid[1]), sid))
}
_, err = os.Stat(path.Join(fp.savePath, string(oldsid[0]), string(oldsid[1]), oldsid))
var f *os.File
if err == nil {
f, err = os.OpenFile(path.Join(fp.savePath, string(oldsid[0]), string(oldsid[1]), oldsid), os.O_RDWR, 0777)
io.Copy(newf, f)
} else if os.IsNotExist(err) {
newf, err = os.Create(path.Join(fp.savePath, string(sid[0]), string(sid[1]), sid))
} else {
return nil, err
}
f.Close()
os.Remove(path.Join(fp.savePath, string(oldsid[0]), string(oldsid[1])))
os.Chtimes(path.Join(fp.savePath, string(sid[0]), string(sid[1]), sid), time.Now(), time.Now())
var kv map[interface{}]interface{}
b, err := ioutil.ReadAll(newf)
if err != nil {
return nil, err
}
if len(b) == 0 {
kv = make(map[interface{}]interface{})
} else {
kv, err = DecodeGob(b)
if err != nil {
return nil, err
}
}
ss := &FileSessionStore{sid: sid, values: kv}
return ss, nil
}
// remove file in save path if expired
func gcpath(path string, info os.FileInfo, err error) error {
if err != nil {
return err
}
if info.IsDir() {
return nil
}
if (info.ModTime().Unix() + gcmaxlifetime) < time.Now().Unix() {
os.Remove(path)
}
return nil
}
type activeSession struct {
total int
}
func (self *activeSession) visit(paths string, f os.FileInfo, err error) error {
if err != nil {
return err
}
if f.IsDir() {
return nil
}
self.total = self.total + 1
return nil
}
func init() {
Register("file", filepder)
}

View File

@ -1,199 +0,0 @@
// Copyright 2013 Beego Authors
//
// Licensed under the Apache License, Version 2.0 (the "License"): you may
// not use this file except in compliance with the License. You may obtain
// a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
// License for the specific language governing permissions and limitations
// under the License.
package session
import (
"container/list"
"net/http"
"sync"
"time"
)
var mempder = &MemProvider{list: list.New(), sessions: make(map[string]*list.Element)}
// memory session store.
// it saved sessions in a map in memory.
type MemSessionStore struct {
sid string //session id
timeAccessed time.Time //last access time
value map[interface{}]interface{} //session store
lock sync.RWMutex
}
// set value to memory session
func (st *MemSessionStore) Set(key, value interface{}) error {
st.lock.Lock()
defer st.lock.Unlock()
st.value[key] = value
return nil
}
// get value from memory session by key
func (st *MemSessionStore) Get(key interface{}) interface{} {
st.lock.RLock()
defer st.lock.RUnlock()
if v, ok := st.value[key]; ok {
return v
} else {
return nil
}
}
// delete in memory session by key
func (st *MemSessionStore) Delete(key interface{}) error {
st.lock.Lock()
defer st.lock.Unlock()
delete(st.value, key)
return nil
}
// clear all values in memory session
func (st *MemSessionStore) Flush() error {
st.lock.Lock()
defer st.lock.Unlock()
st.value = make(map[interface{}]interface{})
return nil
}
// get this id of memory session store
func (st *MemSessionStore) SessionID() string {
return st.sid
}
// Implement method, no used.
func (st *MemSessionStore) SessionRelease(w http.ResponseWriter) {
}
type MemProvider struct {
lock sync.RWMutex // locker
sessions map[string]*list.Element // map in memory
list *list.List // for gc
maxlifetime int64
savePath string
}
// init memory session
func (pder *MemProvider) SessionInit(maxlifetime int64, savePath string) error {
pder.maxlifetime = maxlifetime
pder.savePath = savePath
return nil
}
// get memory session store by sid
func (pder *MemProvider) SessionRead(sid string) (RawStore, error) {
pder.lock.RLock()
if element, ok := pder.sessions[sid]; ok {
go pder.SessionUpdate(sid)
pder.lock.RUnlock()
return element.Value.(*MemSessionStore), nil
} else {
pder.lock.RUnlock()
pder.lock.Lock()
newsess := &MemSessionStore{sid: sid, timeAccessed: time.Now(), value: make(map[interface{}]interface{})}
element := pder.list.PushBack(newsess)
pder.sessions[sid] = element
pder.lock.Unlock()
return newsess, nil
}
}
// check session store exist in memory session by sid
func (pder *MemProvider) SessionExist(sid string) bool {
pder.lock.RLock()
defer pder.lock.RUnlock()
if _, ok := pder.sessions[sid]; ok {
return true
} else {
return false
}
}
// generate new sid for session store in memory session
func (pder *MemProvider) SessionRegenerate(oldsid, sid string) (RawStore, error) {
pder.lock.RLock()
if element, ok := pder.sessions[oldsid]; ok {
go pder.SessionUpdate(oldsid)
pder.lock.RUnlock()
pder.lock.Lock()
element.Value.(*MemSessionStore).sid = sid
pder.sessions[sid] = element
delete(pder.sessions, oldsid)
pder.lock.Unlock()
return element.Value.(*MemSessionStore), nil
} else {
pder.lock.RUnlock()
pder.lock.Lock()
newsess := &MemSessionStore{sid: sid, timeAccessed: time.Now(), value: make(map[interface{}]interface{})}
element := pder.list.PushBack(newsess)
pder.sessions[sid] = element
pder.lock.Unlock()
return newsess, nil
}
}
// delete session store in memory session by id
func (pder *MemProvider) SessionDestroy(sid string) error {
pder.lock.Lock()
defer pder.lock.Unlock()
if element, ok := pder.sessions[sid]; ok {
delete(pder.sessions, sid)
pder.list.Remove(element)
return nil
}
return nil
}
// clean expired session stores in memory session
func (pder *MemProvider) SessionGC() {
pder.lock.RLock()
for {
element := pder.list.Back()
if element == nil {
break
}
if (element.Value.(*MemSessionStore).timeAccessed.Unix() + pder.maxlifetime) < time.Now().Unix() {
pder.lock.RUnlock()
pder.lock.Lock()
pder.list.Remove(element)
delete(pder.sessions, element.Value.(*MemSessionStore).sid)
pder.lock.Unlock()
pder.lock.RLock()
} else {
break
}
}
pder.lock.RUnlock()
}
// get count number of memory session
func (pder *MemProvider) SessionAll() int {
return pder.list.Len()
}
// expand time of session store by id in memory session
func (pder *MemProvider) SessionUpdate(sid string) error {
pder.lock.Lock()
defer pder.lock.Unlock()
if element, ok := pder.sessions[sid]; ok {
element.Value.(*MemSessionStore).timeAccessed = time.Now()
pder.list.MoveToFront(element)
return nil
}
return nil
}
func init() {
Register("memory", mempder)
}

View File

@ -1,59 +0,0 @@
// Copyright 2013 Beego Authors
// Copyright 2014 Unknwon
//
// Licensed under the Apache License, Version 2.0 (the "License"): you may
// not use this file except in compliance with the License. You may obtain
// a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
// License for the specific language governing permissions and limitations
// under the License.
package session
import (
"net/http"
"net/http/httptest"
"strings"
"testing"
. "github.com/smartystreets/goconvey/convey"
)
func TestMem(t *testing.T) {
Convey("Memory provider", t, func() {
config := &Config{
CookieName: "gosessionid",
Gclifetime: 10,
}
globalSessions, err := NewManager("memory", config)
So(err, ShouldBeNil)
go globalSessions.GC()
resp := httptest.NewRecorder()
req, err := http.NewRequest("GET", "/", nil)
So(err, ShouldBeNil)
sess, err := globalSessions.SessionStart(resp, req)
if err != nil {
t.Fatal("start session,", err)
}
defer sess.SessionRelease(resp)
So(sess.Set("username", "Unknwon"), ShouldBeNil)
So(sess.Get("username"), ShouldEqual, "Unknwon")
cookiestr := resp.Header().Get("Set-Cookie")
So(cookiestr, ShouldNotBeEmpty)
parts := strings.Split(strings.TrimSpace(cookiestr), ";")
for _, v := range parts {
nameval := strings.Split(v, "=")
So(nameval[0], ShouldEqual, "gosessionid")
break
}
})
}

View File

@ -1,132 +0,0 @@
// Copyright 2013 Beego Authors
//
// Licensed under the Apache License, Version 2.0 (the "License"): you may
// not use this file except in compliance with the License. You may obtain
// a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
// License for the specific language governing permissions and limitations
// under the License.
package session
import (
"crypto/aes"
"encoding/json"
"testing"
)
func Test_gob(t *testing.T) {
a := make(map[interface{}]interface{})
a["username"] = "astaxie"
a[12] = 234
a["user"] = User{"asta", "xie"}
b, err := EncodeGob(a)
if err != nil {
t.Error(err)
}
c, err := DecodeGob(b)
if err != nil {
t.Error(err)
}
if len(c) == 0 {
t.Error("decodeGob empty")
}
if c["username"] != "astaxie" {
t.Error("decode string error")
}
if c[12] != 234 {
t.Error("decode int error")
}
if c["user"].(User).Username != "asta" {
t.Error("decode struct error")
}
}
type User struct {
Username string
NickName string
}
func TestGenerate(t *testing.T) {
str := generateRandomKey(20)
if len(str) != 20 {
t.Fatal("generate length is not equal to 20")
}
}
func TestCookieEncodeDecode(t *testing.T) {
hashKey := "testhashKey"
blockkey := generateRandomKey(16)
block, err := aes.NewCipher(blockkey)
if err != nil {
t.Fatal("NewCipher:", err)
}
securityName := string(generateRandomKey(20))
val := make(map[interface{}]interface{})
val["name"] = "astaxie"
val["gender"] = "male"
str, err := encodeCookie(block, hashKey, securityName, val)
if err != nil {
t.Fatal("encodeCookie:", err)
}
dst := make(map[interface{}]interface{})
dst, err = decodeCookie(block, hashKey, securityName, str, 3600)
if err != nil {
t.Fatal("decodeCookie", err)
}
if dst["name"] != "astaxie" {
t.Fatal("dst get map error")
}
if dst["gender"] != "male" {
t.Fatal("dst get map error")
}
}
func TestParseConfig(t *testing.T) {
s := `{"cookieName":"gosessionid","gclifetime":3600}`
cf := new(Config)
cf.EnableSetCookie = true
err := json.Unmarshal([]byte(s), cf)
if err != nil {
t.Fatal("parse json error,", err)
}
if cf.CookieName != "gosessionid" {
t.Fatal("parseconfig get cookiename error")
}
if cf.Gclifetime != 3600 {
t.Fatal("parseconfig get gclifetime error")
}
cc := `{"cookieName":"gosessionid","enableSetCookie":false,"gclifetime":3600,"ProviderConfig":"{\"cookieName\":\"gosessionid\",\"securityKey\":\"beegocookiehashkey\"}"}`
cf2 := new(Config)
cf2.EnableSetCookie = true
err = json.Unmarshal([]byte(cc), cf2)
if err != nil {
t.Fatal("parse json error,", err)
}
if cf2.CookieName != "gosessionid" {
t.Fatal("parseconfig get cookiename error")
}
if cf2.Gclifetime != 3600 {
t.Fatal("parseconfig get gclifetime error")
}
if cf2.EnableSetCookie != false {
t.Fatal("parseconfig get enableSetCookie error")
}
cconfig := new(cookieConfig)
err = json.Unmarshal([]byte(cf2.ProviderConfig), cconfig)
if err != nil {
t.Fatal("parse ProviderConfig err,", err)
}
if cconfig.CookieName != "gosessionid" {
t.Fatal("ProviderConfig get cookieName error")
}
if cconfig.SecurityKey != "beegocookiehashkey" {
t.Fatal("ProviderConfig get securityKey error")
}
}

View File

@ -1,231 +0,0 @@
// Copyright 2013 Beego Authors
//
// Licensed under the Apache License, Version 2.0 (the "License"): you may
// not use this file except in compliance with the License. You may obtain
// a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
// License for the specific language governing permissions and limitations
// under the License.
package session
import (
"bytes"
"crypto/cipher"
"crypto/hmac"
"crypto/rand"
"crypto/sha1"
"crypto/subtle"
"encoding/base64"
"encoding/gob"
"errors"
"fmt"
"io"
r "math/rand"
"strconv"
"time"
)
func init() {
gob.Register([]interface{}{})
gob.Register(map[int]interface{}{})
gob.Register(map[string]interface{}{})
gob.Register(map[interface{}]interface{}{})
gob.Register(map[string]string{})
gob.Register(map[int]string{})
gob.Register(map[int]int{})
gob.Register(map[int]int64{})
}
// RandomCreateBytes generate random []byte by specify chars.
func RandomCreateBytes(n int, alphabets ...byte) []byte {
const alphanum = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"
var bytes = make([]byte, n)
var randby bool
if num, err := rand.Read(bytes); num != n || err != nil {
r.Seed(time.Now().UnixNano())
randby = true
}
for i, b := range bytes {
if len(alphabets) == 0 {
if randby {
bytes[i] = alphanum[r.Intn(len(alphanum))]
} else {
bytes[i] = alphanum[b%byte(len(alphanum))]
}
} else {
if randby {
bytes[i] = alphabets[r.Intn(len(alphabets))]
} else {
bytes[i] = alphabets[b%byte(len(alphabets))]
}
}
}
return bytes
}
func EncodeGob(obj map[interface{}]interface{}) ([]byte, error) {
for _, v := range obj {
gob.Register(v)
}
buf := bytes.NewBuffer(nil)
enc := gob.NewEncoder(buf)
err := enc.Encode(obj)
if err != nil {
return []byte(""), err
}
return buf.Bytes(), nil
}
func DecodeGob(encoded []byte) (map[interface{}]interface{}, error) {
buf := bytes.NewBuffer(encoded)
dec := gob.NewDecoder(buf)
var out map[interface{}]interface{}
err := dec.Decode(&out)
if err != nil {
return nil, err
}
return out, nil
}
// generateRandomKey creates a random key with the given strength.
func generateRandomKey(strength int) []byte {
k := make([]byte, strength)
if n, err := io.ReadFull(rand.Reader, k); n != strength || err != nil {
return RandomCreateBytes(strength)
}
return k
}
// Encryption -----------------------------------------------------------------
// encrypt encrypts a value using the given block in counter mode.
//
// A random initialization vector (http://goo.gl/zF67k) with the length of the
// block size is prepended to the resulting ciphertext.
func encrypt(block cipher.Block, value []byte) ([]byte, error) {
iv := generateRandomKey(block.BlockSize())
if iv == nil {
return nil, errors.New("encrypt: failed to generate random iv")
}
// Encrypt it.
stream := cipher.NewCTR(block, iv)
stream.XORKeyStream(value, value)
// Return iv + ciphertext.
return append(iv, value...), nil
}
// decrypt decrypts a value using the given block in counter mode.
//
// The value to be decrypted must be prepended by a initialization vector
// (http://goo.gl/zF67k) with the length of the block size.
func decrypt(block cipher.Block, value []byte) ([]byte, error) {
size := block.BlockSize()
if len(value) > size {
// Extract iv.
iv := value[:size]
// Extract ciphertext.
value = value[size:]
// Decrypt it.
stream := cipher.NewCTR(block, iv)
stream.XORKeyStream(value, value)
return value, nil
}
return nil, errors.New("decrypt: the value could not be decrypted")
}
func encodeCookie(block cipher.Block, hashKey, name string, value map[interface{}]interface{}) (string, error) {
var err error
var b []byte
// 1. EncodeGob.
if b, err = EncodeGob(value); err != nil {
return "", err
}
// 2. Encrypt (optional).
if b, err = encrypt(block, b); err != nil {
return "", err
}
b = encode(b)
// 3. Create MAC for "name|date|value". Extra pipe to be used later.
b = []byte(fmt.Sprintf("%s|%d|%s|", name, time.Now().UTC().Unix(), b))
h := hmac.New(sha1.New, []byte(hashKey))
h.Write(b)
sig := h.Sum(nil)
// Append mac, remove name.
b = append(b, sig...)[len(name)+1:]
// 4. Encode to base64.
b = encode(b)
// Done.
return string(b), nil
}
func decodeCookie(block cipher.Block, hashKey, name, value string, gcmaxlifetime int64) (map[interface{}]interface{}, error) {
// 1. Decode from base64.
b, err := decode([]byte(value))
if err != nil {
return nil, err
}
// 2. Verify MAC. Value is "date|value|mac".
parts := bytes.SplitN(b, []byte("|"), 3)
if len(parts) != 3 {
return nil, errors.New("Decode: invalid value %v")
}
b = append([]byte(name+"|"), b[:len(b)-len(parts[2])]...)
h := hmac.New(sha1.New, []byte(hashKey))
h.Write(b)
sig := h.Sum(nil)
if len(sig) != len(parts[2]) || subtle.ConstantTimeCompare(sig, parts[2]) != 1 {
return nil, errors.New("Decode: the value is not valid")
}
// 3. Verify date ranges.
var t1 int64
if t1, err = strconv.ParseInt(string(parts[0]), 10, 64); err != nil {
return nil, errors.New("Decode: invalid timestamp")
}
t2 := time.Now().UTC().Unix()
if t1 > t2 {
return nil, errors.New("Decode: timestamp is too new")
}
if t1 < t2-gcmaxlifetime {
return nil, errors.New("Decode: expired timestamp")
}
// 4. Decrypt (optional).
b, err = decode(parts[1])
if err != nil {
return nil, err
}
if b, err = decrypt(block, b); err != nil {
return nil, err
}
// 5. DecodeGob.
if dst, err := DecodeGob(b); err != nil {
return nil, err
} else {
return dst, nil
}
}
// Encoding -------------------------------------------------------------------
// encode encodes a value using base64.
func encode(value []byte) []byte {
encoded := make([]byte, base64.URLEncoding.EncodedLen(len(value)))
base64.URLEncoding.Encode(encoded, value)
return encoded
}
// decode decodes a cookie using base64.
func decode(value []byte) ([]byte, error) {
decoded := make([]byte, base64.URLEncoding.DecodedLen(len(value)))
b, err := base64.URLEncoding.Decode(decoded, value)
if err != nil {
return nil, err
}
return decoded[:b], nil
}

View File

@ -16,10 +16,9 @@
// Package session a middleware that provides the session manager of Macaron.
package session
// NOTE: last sync fc6b9ce on Nov 4, 2014.
// NOTE: last sync 000033e on Nov 4, 2014.
import (
"crypto/rand"
"encoding/hex"
"fmt"
"net/http"
@ -29,30 +28,74 @@ import (
"github.com/Unknwon/macaron"
)
const _VERSION = "0.1.1"
func Version() string {
return "0.0.5"
return _VERSION
}
// RawStore is the interface that operates the session data.
type RawStore interface {
Set(key, value interface{}) error //set session value
Get(key interface{}) interface{} //get session value
Delete(key interface{}) error //delete session value
SessionID() string //back current sessionID
SessionRelease(w http.ResponseWriter) // release the resource & save data to provider & return the data
Flush() error //delete all data
// Set sets value to given key in session.
Set(key, value interface{}) error
// Get gets value by given key in session.
Get(key interface{}) interface{}
// Delete delete a key from session.
Delete(key interface{}) error
// ID returns current session ID.
ID() string
// Release releases session resource and save data to provider.
Release() error
// Flush deletes all session data.
Flush() error
}
// Store contains all data for one session process with specific id.
// Store is the interface that contains all data for one session process with specific ID.
type Store interface {
RawStore
GetActiveSession() int
// Read returns raw session store by session ID.
Read(sid string) (RawStore, error)
// Destory deletes a session.
Destory(*macaron.Context) error
// RegenerateId regenerates a session store from old session ID to new one.
RegenerateId(*macaron.Context) (RawStore, error)
// Count counts and returns number of sessions.
Count() int
// GC calls GC to clean expired sessions.
GC()
}
type store struct {
RawStore
*Manager
}
var _ Store = &store{}
// Options represents a struct for specifying configuration options for the session middleware.
type Options struct {
// Name of provider. Default is memory.
// Name of provider. Default is "memory".
Provider string
// Provider configuration string.
Config
// Provider configuration, it's corresponding to provider.
ProviderConfig string
// Cookie name to save session ID. Default is "MacaronSession".
CookieName string
// Cookie path to store. Default is "/".
CookiePath string
// GC interval time in seconds. Default is 3600.
Gclifetime int64
// Max life time in seconds. Default is whatever GC interval time is.
Maxlifetime int64
// Use HTTPS only. Default is false.
Secure bool
// Cookie life time. Default is 0.
CookieLifeTime int
// Cookie domain name. Default is empty.
Domain string
// Session ID length. Default is 16.
IDLength int
// Configuration section name. Default is "session".
Section string
}
func prepareOptions(options []Options) Options {
@ -60,32 +103,266 @@ func prepareOptions(options []Options) Options {
if len(options) > 0 {
opt = options[0]
}
// Defaults
if len(opt.Provider) == 0 {
opt.Provider = "memory"
if len(opt.Section) == 0 {
opt.Section = "session"
}
sec := macaron.Config().Section(opt.Section)
if len(opt.Provider) == 0 {
opt.Provider = sec.Key("PROVIDER").MustString("memory")
}
if len(opt.ProviderConfig) == 0 && opt.Provider == "file" {
opt.ProviderConfig = sec.Key("PROVIDER_CONFIG").MustString("data/sessions")
}
opt.EnableSetCookie = true
if len(opt.CookieName) == 0 {
opt.CookieName = "MacaronSession"
opt.CookieName = sec.Key("COOKIE_NAME").MustString("MacaronSession")
}
if len(opt.CookiePath) == 0 {
opt.CookiePath = "/"
opt.CookiePath = sec.Key("COOKIE_PATH").MustString("/")
}
if opt.Gclifetime == 0 {
opt.Gclifetime = 3600
opt.Gclifetime = sec.Key("GC_INTERVAL_TIME").MustInt64(3600)
}
if opt.Maxlifetime == 0 {
opt.Maxlifetime = opt.Gclifetime
opt.Maxlifetime = sec.Key("MAX_LIFE_TIME").MustInt64(opt.Gclifetime)
}
if opt.SessionIdLength == 0 {
opt.SessionIdLength = 16
if !opt.Secure {
opt.Secure = sec.Key("SECURE").MustBool()
}
if opt.CookieLifeTime == 0 {
opt.CookieLifeTime = sec.Key("COOKIE_LIFE_TIME").MustInt()
}
if len(opt.Domain) == 0 {
opt.Domain = sec.Key("DOMAIN").String()
}
if opt.IDLength == 0 {
opt.IDLength = sec.Key("ID_LENGTH").MustInt(16)
}
return opt
}
// Sessioner is a middleware that maps a session.SessionStore service into the Macaron handler chain.
// An single variadic session.Options struct can be optionally provided to configure.
func Sessioner(options ...Options) macaron.Handler {
opt := prepareOptions(options)
manager, err := NewManager(opt.Provider, opt)
if err != nil {
panic(err)
}
go manager.startGC()
return func(ctx *macaron.Context) {
sess, err := manager.Start(ctx)
if err != nil {
panic("session: " + err.Error())
}
// Get flash.
vals, _ := url.ParseQuery(ctx.GetCookie("macaron_flash"))
if len(vals) > 0 {
f := &Flash{Values: vals}
f.ErrorMsg = f.Get("error")
f.SuccessMsg = f.Get("success")
f.InfoMsg = f.Get("info")
f.WarningMsg = f.Get("warning")
ctx.Data["Flash"] = f
ctx.SetCookie("macaron_flash", "", -1, opt.CookiePath)
}
f := &Flash{ctx, url.Values{}, "", "", "", ""}
ctx.Resp.Before(func(macaron.ResponseWriter) {
if flash := f.Encode(); len(flash) > 0 {
ctx.SetCookie("macaron_flash", flash, 0, opt.CookiePath)
}
})
ctx.Map(f)
s := store{
RawStore: sess,
Manager: manager,
}
ctx.MapTo(s, (*Store)(nil))
ctx.Next()
if sess.Release() != nil {
panic("session: " + err.Error())
}
}
}
// Provider is the interface that provides session manipulations.
type Provider interface {
// Init initializes session provider.
Init(gclifetime int64, config string) error
// Read returns raw session store by session ID.
Read(sid string) (RawStore, error)
// Exist returns true if session with given ID exists.
Exist(sid string) bool
// Destory deletes a session by session ID.
Destory(sid string) error
// Regenerate regenerates a session store from old session ID to new one.
Regenerate(oldsid, sid string) (RawStore, error)
// Count counts and returns number of sessions.
Count() int
// GC calls GC to clean expired sessions.
GC()
}
var providers = make(map[string]Provider)
// Register registers a provider.
func Register(name string, provider Provider) {
if provider == nil {
panic("session: cannot register provider with nil value")
}
if _, dup := providers[name]; dup {
panic(fmt.Errorf("session: cannot register provider '%s' twice", name))
}
providers[name] = provider
}
// _____
// / \ _____ ____ _____ ____ ___________
// / \ / \\__ \ / \\__ \ / ___\_/ __ \_ __ \
// / Y \/ __ \| | \/ __ \_/ /_/ > ___/| | \/
// \____|__ (____ /___| (____ /\___ / \___ >__|
// \/ \/ \/ \//_____/ \/
// Manager represents a struct that contains session provider and its configuration.
type Manager struct {
provider Provider
opt Options
}
// NewManager creates and returns a new session manager by given provider name and configuration.
// It panics when given provider isn't registered.
func NewManager(name string, opt Options) (*Manager, error) {
p, ok := providers[name]
if !ok {
return nil, fmt.Errorf("session: unknown provider %q(forgotten import?)", name)
}
if err := p.Init(opt.Maxlifetime, opt.ProviderConfig); err != nil {
return nil, err
}
return &Manager{p, opt}, nil
}
// sessionId generates a new session ID with rand string, unix nano time, remote addr by hash function.
func (m *Manager) sessionId() string {
return hex.EncodeToString(generateRandomKey(m.opt.IDLength))
}
// Start starts a session by generating new one
// or retrieve existence one by reading session ID from HTTP request if it's valid.
func (m *Manager) Start(ctx *macaron.Context) (RawStore, error) {
sid := ctx.GetCookie(m.opt.CookieName)
if len(sid) > 0 && m.provider.Exist(sid) {
return m.provider.Read(sid)
}
sid = m.sessionId()
sess, err := m.provider.Read(sid)
if err != nil {
return nil, err
}
cookie := &http.Cookie{
Name: m.opt.CookieName,
Value: sid,
Path: m.opt.CookiePath,
HttpOnly: true,
Secure: m.opt.Secure,
Domain: m.opt.Domain,
}
if m.opt.CookieLifeTime >= 0 {
cookie.MaxAge = m.opt.CookieLifeTime
}
http.SetCookie(ctx.Resp, cookie)
ctx.Req.AddCookie(cookie)
return sess, nil
}
// Read returns raw session store by session ID.
func (m *Manager) Read(sid string) (RawStore, error) {
return m.provider.Read(sid)
}
// Destory deletes a session by given ID.
func (m *Manager) Destory(ctx *macaron.Context) error {
sid := ctx.GetCookie(m.opt.CookieName)
if len(sid) == 0 {
return nil
}
if err := m.provider.Destory(sid); err != nil {
return err
}
cookie := &http.Cookie{
Name: m.opt.CookieName,
Path: m.opt.CookiePath,
HttpOnly: true,
Expires: time.Now(),
MaxAge: -1,
}
http.SetCookie(ctx.Resp, cookie)
return nil
}
// RegenerateId regenerates a session store from old session ID to new one.
func (m *Manager) RegenerateId(ctx *macaron.Context) (sess RawStore, err error) {
sid := m.sessionId()
oldsid := ctx.GetCookie(m.opt.CookieName)
if len(oldsid) == 0 {
sess, err = m.provider.Read(oldsid)
if err != nil {
return nil, err
}
} else {
sess, err = m.provider.Regenerate(oldsid, sid)
if err != nil {
return nil, err
}
}
ck := &http.Cookie{
Name: m.opt.CookieName,
Value: sid,
Path: m.opt.CookiePath,
HttpOnly: true,
Secure: m.opt.Secure,
Domain: m.opt.Domain,
}
if m.opt.CookieLifeTime >= 0 {
ck.MaxAge = m.opt.CookieLifeTime
}
http.SetCookie(ctx.Resp, ck)
ctx.Req.AddCookie(ck)
return sess, nil
}
// Count counts and returns number of sessions.
func (m *Manager) Count() int {
return m.provider.Count()
}
// GC starts GC job in a certain period.
func (m *Manager) GC() {
m.provider.GC()
}
// startGC starts GC job in a certain period.
func (m *Manager) startGC() {
m.GC()
time.AfterFunc(time.Duration(m.opt.Gclifetime)*time.Second, func() { m.startGC() })
}
// SetSecure indicates whether to set cookie with HTTPS or not.
func (m *Manager) SetSecure(secure bool) {
m.opt.Secure = secure
}
// ___________.____ _____ _________ ___ ___
// \_ _____/| | / _ \ / _____// | \
// | __) | | / /_\ \ \_____ \/ ~ \
@ -132,289 +409,3 @@ func (f *Flash) Success(msg string, current ...bool) {
f.SuccessMsg = msg
f.set("success", msg, current...)
}
type store struct {
RawStore
*Manager
}
// Sessioner is a middleware that maps a session.SessionStore service into the Macaron handler chain.
// An single variadic session.Options struct can be optionally provided to configure.
func Sessioner(options ...Options) macaron.Handler {
opt := prepareOptions(options)
manager, err := NewManager(opt.Provider, &opt.Config)
if err != nil {
panic(err)
}
go manager.GC()
return func(ctx *macaron.Context) {
// FIXME: should I panic for error?
sess, _ := manager.SessionStart(ctx.Resp, ctx.Req.Request)
// Get flash.
vals, _ := url.ParseQuery(ctx.GetCookie("macaron_flash"))
if len(vals) > 0 {
f := &Flash{Values: vals}
f.ErrorMsg = f.Get("error")
f.SuccessMsg = f.Get("success")
f.InfoMsg = f.Get("info")
f.WarningMsg = f.Get("warning")
ctx.Data["Flash"] = f
ctx.SetCookie("macaron_flash", "", -1, opt.CookiePath)
}
f := &Flash{ctx, url.Values{}, "", "", "", ""}
ctx.Resp.Before(func(macaron.ResponseWriter) {
sess.SessionRelease(ctx.Resp)
if flash := f.Encode(); len(flash) > 0 {
ctx.SetCookie("macaron_flash", flash, 0, opt.CookiePath)
}
})
ctx.Map(f)
s := store{
RawStore: sess,
Manager: manager,
}
ctx.MapTo(s, (*Store)(nil))
}
}
// Provider contains global session methods and saved SessionStores.
// it can operate a SessionStore by its id.
type Provider interface {
SessionInit(gclifetime int64, config string) error
SessionRead(sid string) (RawStore, error)
SessionExist(sid string) bool
SessionRegenerate(oldsid, sid string) (RawStore, error)
SessionDestroy(sid string) error
SessionAll() int //get all active session
SessionGC()
}
var provides = make(map[string]Provider)
// Register makes a session provide available by the provided name.
// If Register is called twice with the same name or if driver is nil,
// it panics.
func Register(name string, provide Provider) {
if provide == nil {
panic("session: Register provide is nil")
}
if _, dup := provides[name]; dup {
panic("session: Register called twice for provider " + name)
}
provides[name] = provide
}
type Config struct {
CookieName string `json:"cookieName"`
CookiePath string `json:"cookiePath"`
EnableSetCookie bool `json:"enableSetCookie,omitempty"`
Gclifetime int64 `json:"gclifetime"`
Maxlifetime int64 `json:"maxLifetime"`
Secure bool `json:"secure"`
CookieLifeTime int `json:"cookieLifeTime"`
ProviderConfig string `json:"providerConfig"`
Domain string `json:"domain"`
SessionIdLength int64 `json:"sessionIdLength"`
}
// Manager contains Provider and its configuration.
type Manager struct {
provider Provider
config *Config
}
// Create new Manager with provider name and json config string.
// provider name:
// 1. cookie
// 2. file
// 3. memory
// 4. redis
// 5. mysql
// json config:
// 1. is https default false
// 2. hashfunc default sha1
// 3. hashkey default beegosessionkey
// 4. maxage default is none
func NewManager(provideName string, config *Config) (*Manager, error) {
provider, ok := provides[provideName]
if !ok {
return nil, fmt.Errorf("session: unknown provide %q (forgotten import?)", provideName)
}
config.EnableSetCookie = true
if config.Maxlifetime == 0 {
config.Maxlifetime = config.Gclifetime
}
if err := provider.SessionInit(config.Maxlifetime, config.ProviderConfig); err != nil {
return nil, err
}
return &Manager{
provider: provider,
config: config,
}, nil
}
// Start session. generate or read the session id from http request.
// if session id exists, return SessionStore with this id.
func (manager *Manager) SessionStart(w http.ResponseWriter, r *http.Request) (session RawStore, _ error) {
cookie, err := r.Cookie(manager.config.CookieName)
if err != nil || len(cookie.Value) == 0 {
sid, err := manager.sessionId(r)
if err != nil {
return nil, err
}
session, err = manager.provider.SessionRead(sid)
if err != nil {
return nil, err
}
cookie = &http.Cookie{Name: manager.config.CookieName,
Value: url.QueryEscape(sid),
Path: manager.config.CookiePath,
HttpOnly: true,
Secure: manager.config.Secure,
Domain: manager.config.Domain,
}
if manager.config.CookieLifeTime >= 0 {
cookie.MaxAge = manager.config.CookieLifeTime
}
if manager.config.EnableSetCookie {
http.SetCookie(w, cookie)
}
r.AddCookie(cookie)
} else {
sid, err := url.QueryUnescape(cookie.Value)
if err != nil {
return nil, err
}
if manager.provider.SessionExist(sid) {
session, err = manager.provider.SessionRead(sid)
if err != nil {
return nil, err
}
} else {
sid, err = manager.sessionId(r)
if err != nil {
return nil, err
}
session, err = manager.provider.SessionRead(sid)
if err != nil {
return nil, err
}
cookie = &http.Cookie{Name: manager.config.CookieName,
Value: url.QueryEscape(sid),
Path: manager.config.CookiePath,
HttpOnly: true,
Secure: manager.config.Secure,
Domain: manager.config.Domain,
}
if manager.config.CookieLifeTime >= 0 {
cookie.MaxAge = manager.config.CookieLifeTime
}
if manager.config.EnableSetCookie {
http.SetCookie(w, cookie)
}
r.AddCookie(cookie)
}
}
return session, nil
}
// Destroy session by its id in http request cookie.
func (manager *Manager) SessionDestroy(w http.ResponseWriter, r *http.Request) {
cookie, err := r.Cookie(manager.config.CookieName)
if err != nil || cookie.Value == "" {
return
} else {
manager.provider.SessionDestroy(cookie.Value)
expiration := time.Now()
cookie := http.Cookie{Name: manager.config.CookieName,
Path: manager.config.CookiePath,
HttpOnly: true,
Expires: expiration,
MaxAge: -1}
http.SetCookie(w, &cookie)
}
}
// Get SessionStore by its id.
func (manager *Manager) GetSessionStore(sid string) (sessions RawStore, err error) {
sessions, err = manager.provider.SessionRead(sid)
return
}
// Start session gc process.
// it can do gc in times after gc lifetime.
func (manager *Manager) GC() {
manager.provider.SessionGC()
time.AfterFunc(time.Duration(manager.config.Gclifetime)*time.Second, func() { manager.GC() })
}
// Regenerate a session id for this SessionStore who's id is saving in http request.
func (manager *Manager) SessionRegenerateId(w http.ResponseWriter, r *http.Request) (session RawStore) {
sid, err := manager.sessionId(r)
if err != nil {
return nil
}
cookie, err := r.Cookie(manager.config.CookieName)
if err != nil && cookie.Value == "" {
// delete old cookie
session, err = manager.provider.SessionRead(sid)
if err != nil {
return nil
}
cookie = &http.Cookie{Name: manager.config.CookieName,
Value: url.QueryEscape(sid),
Path: manager.config.CookiePath,
HttpOnly: true,
Secure: manager.config.Secure,
Domain: manager.config.Domain,
}
} else {
oldsid, err := url.QueryUnescape(cookie.Value)
if err != nil {
return nil
}
session, err = manager.provider.SessionRegenerate(oldsid, sid)
if err != nil {
return nil
}
cookie.Value = url.QueryEscape(sid)
cookie.HttpOnly = true
cookie.Path = "/"
}
if manager.config.CookieLifeTime >= 0 {
cookie.MaxAge = manager.config.CookieLifeTime
}
http.SetCookie(w, cookie)
r.AddCookie(cookie)
return session
}
// Get all active sessions count number.
func (manager *Manager) GetActiveSession() int {
return manager.provider.SessionAll()
}
// Set cookie with https.
func (manager *Manager) SetSecure(secure bool) {
manager.config.Secure = secure
}
// generate session id with rand string, unix nano time, remote addr by hash function.
func (manager *Manager) sessionId(r *http.Request) (string, error) {
b := make([]byte, manager.config.SessionIdLength)
n, err := rand.Read(b)
if n != len(b) || err != nil {
return "", fmt.Errorf("fail to read from the system CSPRNG.")
}
return hex.EncodeToString(b), nil
}

Binary file not shown.

View File

@ -37,10 +37,7 @@ func newMacaron() *macaron.Macaron {
mapStatic(m, "public/app", "app")
mapStatic(m, "public/img", "img")
m.Use(session.Sessioner(session.Options{
Provider: setting.SessionProvider,
Config: *setting.SessionConfig,
}))
m.Use(session.Sessioner(setting.SessionOptions))
m.Use(macaron.Renderer(macaron.RenderOptions{
Directory: path.Join(setting.StaticRootPath, "views"),

View File

@ -54,8 +54,7 @@ var (
StaticRootPath string
// Session settings.
SessionProvider string
SessionConfig *session.Config
SessionOptions session.Options
// Global setting objects.
Cfg *goconfig.ConfigFile
@ -149,19 +148,18 @@ func NewConfigContext() {
}
func initSessionService() {
SessionProvider = Cfg.MustValueRange("session", "provider", "memory", []string{"memory", "file"})
SessionConfig = new(session.Config)
SessionConfig.ProviderConfig = strings.Trim(Cfg.MustValue("session", "provider_config"), "\" ")
SessionConfig.CookieName = Cfg.MustValue("session", "cookie_name", "grafana_pro_sess")
SessionConfig.CookiePath = AppSubUrl
SessionConfig.Secure = Cfg.MustBool("session", "cookie_secure")
SessionConfig.EnableSetCookie = Cfg.MustBool("session", "enable_set_cookie", true)
SessionConfig.Gclifetime = Cfg.MustInt64("session", "gc_interval_time", 86400)
SessionConfig.Maxlifetime = Cfg.MustInt64("session", "session_life_time", 86400)
SessionOptions = session.Options{}
SessionOptions.Provider = Cfg.MustValueRange("session", "provider", "memory", []string{"memory", "file"})
SessionOptions.ProviderConfig = strings.Trim(Cfg.MustValue("session", "provider_config"), "\" ")
SessionOptions.CookieName = Cfg.MustValue("session", "cookie_name", "grafana_pro_sess")
SessionOptions.CookiePath = AppSubUrl
SessionOptions.Secure = Cfg.MustBool("session", "cookie_secure")
SessionOptions.Gclifetime = Cfg.MustInt64("session", "gc_interval_time", 86400)
SessionOptions.Maxlifetime = Cfg.MustInt64("session", "session_life_time", 86400)
if SessionProvider == "file" {
os.MkdirAll(path.Dir(SessionConfig.ProviderConfig), os.ModePerm)
if SessionOptions.Provider == "file" {
os.MkdirAll(path.Dir(SessionOptions.ProviderConfig), os.ModePerm)
}
log.Info("Session Service Enabled")