mirror of
https://github.com/kataras/iris.git
synced 2025-01-23 18:51:03 +01:00
Merge pull request #693 from corebreaker/session-expiration
Fix up for sessions Former-commit-id: 12b18902b4776335053b4d971ec564a9659a4c2d
This commit is contained in:
commit
351f099ad6
|
@ -65,5 +65,10 @@ func main() {
|
|||
sess.Destroy(ctx)
|
||||
})
|
||||
|
||||
app.Get("/update", func(ctx context.Context) {
|
||||
// updates expire date with a new date
|
||||
sess.ShiftExpiraton(ctx)
|
||||
})
|
||||
|
||||
app.Run(iris.Addr(":8080"))
|
||||
}
|
||||
|
|
|
@ -62,10 +62,16 @@ func newApp() *iris.Application {
|
|||
mySessions.Start(ctx).Clear()
|
||||
})
|
||||
|
||||
app.Get("/update", func(ctx context.Context) {
|
||||
// updates expire date with a new date
|
||||
mySessions.ShiftExpiraton(ctx)
|
||||
})
|
||||
|
||||
app.Get("/destroy", func(ctx context.Context) {
|
||||
//destroy, removes the entire session data and cookie
|
||||
mySessions.Destroy(ctx)
|
||||
}) // Note about destroy:
|
||||
})
|
||||
// Note about destroy:
|
||||
//
|
||||
// You can destroy a session outside of a handler too, using the:
|
||||
// mySessions.DestroyByID
|
||||
|
|
|
@ -69,6 +69,11 @@ func main() {
|
|||
sess.Start(ctx).Clear()
|
||||
})
|
||||
|
||||
app.Get("/update", func(ctx context.Context) {
|
||||
// updates expire date
|
||||
sess.ShiftExpiraton(ctx)
|
||||
})
|
||||
|
||||
app.Get("/destroy", func(ctx context.Context) {
|
||||
|
||||
//destroy, removes the entire session data and cookie
|
||||
|
|
|
@ -1,17 +1,24 @@
|
|||
package sessions
|
||||
|
||||
import "time"
|
||||
|
||||
// Database is the interface which all session databases should implement
|
||||
// By design it doesn't support any type of cookie session like other frameworks,
|
||||
// I want to protect you, believe me, no context access (although we could)
|
||||
// The scope of the database is to session somewhere the sessions in order to
|
||||
// keep them after restarting the server, nothing more.
|
||||
// the values are sessiond by the underline session, the check for new sessions, or
|
||||
// 'this session value should added' are made automatically you are able just to set the values to your backend database with Load function.
|
||||
// the values are sessions by the underline session, the check for new sessions, or
|
||||
// 'this session value should added' are made automatically
|
||||
// you are able just to set the values to your backend database with Load function.
|
||||
// session database doesn't have any write or read access to the session, the loading of
|
||||
// the initial data is done by the Load(string) map[string]interfface{} function
|
||||
// the initial data is done by the Load(string) (map[string]interfface{}, *time.Time) function
|
||||
// synchronization are made automatically, you can register more than one session database
|
||||
// but the first non-empty Load return data will be used as the session values.
|
||||
// The Expire Date is given with data to save because the session entry must keep trace
|
||||
// of the expire date in the case of the server is restarted. So the server will recover
|
||||
// expiration state of session entry and it will track the expiration again.
|
||||
// If expireDate is nil, that's means that there is no expire date.
|
||||
type Database interface {
|
||||
Load(string) map[string]interface{}
|
||||
Update(string, map[string]interface{})
|
||||
Load(sid string) (datas map[string]interface{}, expireDate *time.Time)
|
||||
Update(sid string, datas map[string]interface{}, expireDate *time.Time)
|
||||
}
|
||||
|
|
|
@ -36,43 +36,76 @@ func (p *provider) RegisterDatabase(db Database) {
|
|||
p.mu.Unlock()
|
||||
}
|
||||
|
||||
// newSession returns a new session from sessionid
|
||||
func (p *provider) newSession(sid string, expires time.Duration) *Session {
|
||||
sess := &Session{
|
||||
sid: sid,
|
||||
provider: p,
|
||||
values: p.loadSessionValuesFromDB(sid),
|
||||
flashes: make(map[string]*flashMessage),
|
||||
}
|
||||
// startAutoDestroy start a task which destoy the session when expire date is reached,
|
||||
// but only if `expires` parameter is positive. It updates the expire date of the session from `expires` parameter.
|
||||
func (p *provider) startAutoDestroy(s *Session, expires time.Duration) bool {
|
||||
res := expires > 0
|
||||
if res { // if not unlimited life duration and no -1 (cookie remove action is based on browser's session)
|
||||
expireDate := time.Now().Add(expires)
|
||||
|
||||
if expires > 0 { // if not unlimited life duration and no -1 (cookie remove action is based on browser's session)
|
||||
time.AfterFunc(expires, func() {
|
||||
s.expireAt = &expireDate
|
||||
s.timer = time.AfterFunc(expires, func() {
|
||||
// the destroy makes the check if this session is exists then or not,
|
||||
// this is used to destroy the session from the server-side also
|
||||
// it's good to have here for security reasons, I didn't add it on the gc function to separate its action
|
||||
p.Destroy(sid)
|
||||
p.Destroy(s.sid)
|
||||
})
|
||||
}
|
||||
|
||||
return res
|
||||
}
|
||||
|
||||
// newSession returns a new session from sessionid
|
||||
func (p *provider) newSession(sid string, expires time.Duration) *Session {
|
||||
values, expireAt := p.loadSessionValuesFromDB(sid)
|
||||
|
||||
sess := &Session{
|
||||
sid: sid,
|
||||
provider: p,
|
||||
values: values,
|
||||
flashes: make(map[string]*flashMessage),
|
||||
expireAt: expireAt,
|
||||
}
|
||||
|
||||
if (len(values) > 0) && (sess.expireAt != nil) {
|
||||
// Restore expiration state
|
||||
// However, if session save in database has no expiration date,
|
||||
// therefore the expiration will be reinitialised with session configuration
|
||||
expires = sess.expireAt.Sub(time.Now())
|
||||
}
|
||||
|
||||
p.startAutoDestroy(sess, expires)
|
||||
|
||||
return sess
|
||||
}
|
||||
|
||||
// can return nil
|
||||
func (p *provider) loadSessionValuesFromDB(sid string) memstore.Store {
|
||||
// can return nil memstore
|
||||
func (p *provider) loadSessionValuesFromDB(sid string) (memstore.Store, *time.Time) {
|
||||
var store memstore.Store
|
||||
var expireDate *time.Time
|
||||
|
||||
for i, n := 0, len(p.databases); i < n; i++ {
|
||||
if dbValues := p.databases[i].Load(sid); dbValues != nil && len(dbValues) > 0 {
|
||||
dbValues, currentExpireDate := p.databases[i].Load(sid)
|
||||
if dbValues != nil && len(dbValues) > 0 {
|
||||
for k, v := range dbValues {
|
||||
store.Set(k, v)
|
||||
}
|
||||
}
|
||||
|
||||
if (currentExpireDate != nil) && ((expireDate == nil) || expireDate.After(*currentExpireDate)) {
|
||||
expireDate = currentExpireDate
|
||||
}
|
||||
}
|
||||
return store
|
||||
|
||||
// Check if session has already expired
|
||||
if (expireDate != nil) && expireDate.Before(time.Now()) {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
return store, expireDate
|
||||
}
|
||||
|
||||
func (p *provider) updateDatabases(sid string, store memstore.Store) {
|
||||
|
||||
func (p *provider) updateDatabases(sess *Session, store memstore.Store) {
|
||||
if l := store.Len(); l > 0 {
|
||||
mapValues := make(map[string]interface{}, l)
|
||||
|
||||
|
@ -81,7 +114,7 @@ func (p *provider) updateDatabases(sid string, store memstore.Store) {
|
|||
})
|
||||
|
||||
for i, n := 0, len(p.databases); i < n; i++ {
|
||||
p.databases[i].Update(sid, mapValues)
|
||||
p.databases[i].Update(sess.sid, mapValues, sess.expireAt)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -95,17 +128,50 @@ func (p *provider) Init(sid string, expires time.Duration) *Session {
|
|||
return newSession
|
||||
}
|
||||
|
||||
// UpdateExpiraton update expire date of a session, plus it updates destroy task
|
||||
func (p *provider) UpdateExpiraton(sid string, expires time.Duration) (done bool) {
|
||||
if expires <= 0 {
|
||||
return false
|
||||
}
|
||||
|
||||
p.mu.Lock()
|
||||
sess, found := p.sessions[sid]
|
||||
p.mu.Unlock()
|
||||
|
||||
if !found {
|
||||
return false
|
||||
}
|
||||
|
||||
if sess.timer == nil {
|
||||
return p.startAutoDestroy(sess, expires)
|
||||
} else {
|
||||
if expires <= 0 {
|
||||
sess.timer.Stop()
|
||||
sess.timer = nil
|
||||
sess.expireAt = nil
|
||||
} else {
|
||||
expireDate := time.Now().Add(expires)
|
||||
|
||||
sess.expireAt = &expireDate
|
||||
sess.timer.Reset(expires)
|
||||
}
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
// Read returns the store which sid parameter belongs
|
||||
func (p *provider) Read(sid string, expires time.Duration) *Session {
|
||||
p.mu.Lock()
|
||||
if sess, found := p.sessions[sid]; found {
|
||||
sess.runFlashGC() // run the flash messages GC, new request here of existing session
|
||||
p.mu.Unlock()
|
||||
|
||||
return sess
|
||||
}
|
||||
p.mu.Unlock()
|
||||
return p.Init(sid, expires) // if not found create new
|
||||
|
||||
return p.Init(sid, expires) // if not found create new
|
||||
}
|
||||
|
||||
// Destroy destroys the session, removes all sessions and flash values,
|
||||
|
@ -116,8 +182,12 @@ func (p *provider) Destroy(sid string) {
|
|||
if sess, found := p.sessions[sid]; found {
|
||||
sess.values = nil
|
||||
sess.flashes = nil
|
||||
if sess.timer != nil {
|
||||
sess.timer.Stop()
|
||||
}
|
||||
|
||||
delete(p.sessions, sid)
|
||||
p.updateDatabases(sid, nil)
|
||||
p.updateDatabases(sess, nil)
|
||||
}
|
||||
p.mu.Unlock()
|
||||
|
||||
|
@ -129,8 +199,12 @@ func (p *provider) Destroy(sid string) {
|
|||
func (p *provider) DestroyAll() {
|
||||
p.mu.Lock()
|
||||
for _, sess := range p.sessions {
|
||||
if sess.timer != nil {
|
||||
sess.timer.Stop()
|
||||
}
|
||||
|
||||
delete(p.sessions, sess.ID())
|
||||
p.updateDatabases(sess.ID(), nil)
|
||||
p.updateDatabases(sess, nil)
|
||||
}
|
||||
p.mu.Unlock()
|
||||
|
||||
|
|
|
@ -17,6 +17,7 @@ type (
|
|||
// This is what will be returned when sess := sessions.Start().
|
||||
Session struct {
|
||||
sid string
|
||||
isNew bool
|
||||
values memstore.Store // here are the real values
|
||||
// we could set the flash messages inside values but this will bring us more problems
|
||||
// because of session databases and because of
|
||||
|
@ -26,7 +27,8 @@ type (
|
|||
// NOTE: flashes are not managed by third-party, only inside session struct.
|
||||
flashes map[string]*flashMessage
|
||||
mu sync.RWMutex
|
||||
createdAt time.Time
|
||||
expireAt *time.Time // nil pointer means no expire date
|
||||
timer *time.Timer
|
||||
provider *provider
|
||||
}
|
||||
|
||||
|
@ -42,6 +44,27 @@ func (s *Session) ID() string {
|
|||
return s.sid
|
||||
}
|
||||
|
||||
// IsNew returns true if is's a new session
|
||||
func (s *Session) IsNew() bool {
|
||||
return s.isNew
|
||||
}
|
||||
|
||||
// HasExpireDate test if this session has an expire date, if not, this session never expires
|
||||
func (s *Session) HasExpireDate() bool {
|
||||
return s.expireAt != nil
|
||||
}
|
||||
|
||||
// GetExpireDate get the expire date, if this session has no expire date, the returned value has the zero value
|
||||
func (s *Session) GetExpireDate() time.Time {
|
||||
var res time.Time
|
||||
|
||||
if s.expireAt != nil {
|
||||
res = *s.expireAt
|
||||
}
|
||||
|
||||
return res
|
||||
}
|
||||
|
||||
// Get returns a value based on its "key".
|
||||
func (s *Session) Get(key string) interface{} {
|
||||
s.mu.RLock()
|
||||
|
@ -267,6 +290,7 @@ func (s *Session) set(key string, value interface{}, immutable bool) {
|
|||
s.mu.Unlock()
|
||||
|
||||
s.updateDatabases()
|
||||
s.isNew = false
|
||||
}
|
||||
|
||||
// Set fills the session with an entry"value", based on its "key".
|
||||
|
@ -318,11 +342,15 @@ func (s *Session) Delete(key string) bool {
|
|||
s.mu.Unlock()
|
||||
|
||||
s.updateDatabases()
|
||||
if removed {
|
||||
s.isNew = false
|
||||
}
|
||||
|
||||
return removed
|
||||
}
|
||||
|
||||
func (s *Session) updateDatabases() {
|
||||
s.provider.updateDatabases(s.sid, s.values)
|
||||
s.provider.updateDatabases(s, s.values)
|
||||
}
|
||||
|
||||
// DeleteFlash removes a flash message by its key.
|
||||
|
@ -339,6 +367,7 @@ func (s *Session) Clear() {
|
|||
s.mu.Unlock()
|
||||
|
||||
s.updateDatabases()
|
||||
s.isNew = false
|
||||
}
|
||||
|
||||
// ClearFlashes removes all flash messages.
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
package redis
|
||||
|
||||
import (
|
||||
"time"
|
||||
"bytes"
|
||||
"encoding/gob"
|
||||
|
||||
|
@ -23,7 +24,7 @@ func (d *Database) Config() *service.Config {
|
|||
}
|
||||
|
||||
// Load loads the values to the underline.
|
||||
func (d *Database) Load(sid string) map[string]interface{} {
|
||||
func (d *Database) Load(sid string) (datas map[string]interface{}, expireDate *time.Time) {
|
||||
values := make(map[string]interface{})
|
||||
|
||||
if !d.redis.Connected { //yes, check every first time's session for valid redis connection
|
||||
|
@ -38,6 +39,7 @@ func (d *Database) Load(sid string) map[string]interface{} {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
//fetch the values from this session id and copy-> store them
|
||||
val, err := d.redis.GetBytes(sid)
|
||||
if err == nil {
|
||||
|
@ -45,8 +47,19 @@ func (d *Database) Load(sid string) map[string]interface{} {
|
|||
DeserializeBytes(val, &values)
|
||||
}
|
||||
|
||||
return values
|
||||
datas, _ = values["session-data"].(map[string]interface{})
|
||||
|
||||
dbExpireDateValue, exists := values["expire-date"]
|
||||
if !exists {
|
||||
return
|
||||
}
|
||||
|
||||
expireDateValue, ok := dbExpireDateValue.(time.Time)
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
|
||||
return datas, &expireDateValue
|
||||
}
|
||||
|
||||
// serialize the values to be stored as strings inside the Redis, we panic at any serialization error here
|
||||
|
@ -60,11 +73,17 @@ func serialize(values map[string]interface{}) []byte {
|
|||
}
|
||||
|
||||
// Update updates the real redis store
|
||||
func (d *Database) Update(sid string, newValues map[string]interface{}) {
|
||||
func (d *Database) Update(sid string, newValues map[string]interface{}, expireDate *time.Time) {
|
||||
if len(newValues) == 0 {
|
||||
go d.redis.Delete(sid)
|
||||
} else {
|
||||
go d.redis.Set(sid, serialize(newValues)) //set/update all the values
|
||||
datas := map[string]interface{}{"session-data": newValues}
|
||||
if expireDate != nil {
|
||||
datas["expire-date"] = *expireDate
|
||||
}
|
||||
|
||||
//set/update all the values
|
||||
go d.redis.Set(sid, serialize(datas))
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -35,6 +35,70 @@ func (s *Sessions) UseDatabase(db Database) {
|
|||
s.provider.RegisterDatabase(db)
|
||||
}
|
||||
|
||||
// updateCookie gains the ability of updating the session browser cookie to any method which wants to update it
|
||||
func (s *Sessions) updateCookie(sid string, ctx context.Context, expires time.Duration) {
|
||||
cookie := &http.Cookie{}
|
||||
|
||||
// The RFC makes no mention of encoding url value, so here I think to encode both sessionid key and the value using the safe(to put and to use as cookie) url-encoding
|
||||
cookie.Name = s.config.Cookie
|
||||
|
||||
cookie.Value = sid
|
||||
cookie.Path = "/"
|
||||
if !s.config.DisableSubdomainPersistence {
|
||||
|
||||
requestDomain := ctx.Request().URL.Host
|
||||
if portIdx := strings.IndexByte(requestDomain, ':'); portIdx > 0 {
|
||||
requestDomain = requestDomain[0:portIdx]
|
||||
}
|
||||
if IsValidCookieDomain(requestDomain) {
|
||||
|
||||
// RFC2109, we allow level 1 subdomains, but no further
|
||||
// if we have localhost.com , we want the localhost.cos.
|
||||
// so if we have something like: mysubdomain.localhost.com we want the localhost here
|
||||
// if we have mysubsubdomain.mysubdomain.localhost.com we want the .mysubdomain.localhost.com here
|
||||
// slow things here, especially the 'replace' but this is a good and understable( I hope) way to get the be able to set cookies from subdomains & domain with 1-level limit
|
||||
if dotIdx := strings.LastIndexByte(requestDomain, '.'); dotIdx > 0 {
|
||||
// is mysubdomain.localhost.com || mysubsubdomain.mysubdomain.localhost.com
|
||||
s := requestDomain[0:dotIdx] // set mysubdomain.localhost || mysubsubdomain.mysubdomain.localhost
|
||||
if secondDotIdx := strings.LastIndexByte(s, '.'); secondDotIdx > 0 {
|
||||
//is mysubdomain.localhost || mysubsubdomain.mysubdomain.localhost
|
||||
s = s[secondDotIdx+1:] // set to localhost || mysubdomain.localhost
|
||||
}
|
||||
// replace the s with the requestDomain before the domain's siffux
|
||||
subdomainSuff := strings.LastIndexByte(requestDomain, '.')
|
||||
if subdomainSuff > len(s) { // if it is actual exists as subdomain suffix
|
||||
requestDomain = strings.Replace(requestDomain, requestDomain[0:subdomainSuff], s, 1) // set to localhost.com || mysubdomain.localhost.com
|
||||
}
|
||||
}
|
||||
// finally set the .localhost.com (for(1-level) || .mysubdomain.localhost.com (for 2-level subdomain allow)
|
||||
cookie.Domain = "." + requestDomain // . to allow persistence
|
||||
}
|
||||
}
|
||||
|
||||
cookie.HttpOnly = true
|
||||
// MaxAge=0 means no 'Max-Age' attribute specified.
|
||||
// MaxAge<0 means delete cookie now, equivalently 'Max-Age: 0'
|
||||
// MaxAge>0 means Max-Age attribute present and given in seconds
|
||||
if expires >= 0 {
|
||||
if expires == 0 { // unlimited life
|
||||
cookie.Expires = CookieExpireUnlimited
|
||||
} else { // > 0
|
||||
cookie.Expires = time.Now().Add(expires)
|
||||
}
|
||||
cookie.MaxAge = int(cookie.Expires.Sub(time.Now()).Seconds())
|
||||
}
|
||||
|
||||
// set the cookie to secure if this is a tls wrapped request
|
||||
// and the configuration allows it.
|
||||
if ctx.Request().TLS != nil && s.config.CookieSecureTLS {
|
||||
cookie.Secure = true
|
||||
}
|
||||
|
||||
// encode the session id cookie client value right before send it.
|
||||
cookie.Value = s.encodeCookieValue(cookie.Value)
|
||||
AddCookie(ctx, cookie)
|
||||
}
|
||||
|
||||
// Start should start the session for the particular request.
|
||||
func (s *Sessions) Start(ctx context.Context) *Session {
|
||||
cookieValue := GetCookie(ctx, s.config.Cookie)
|
||||
|
@ -43,74 +107,34 @@ func (s *Sessions) Start(ctx context.Context) *Session {
|
|||
sid := s.config.SessionIDGenerator()
|
||||
|
||||
sess := s.provider.Init(sid, s.config.Expires)
|
||||
cookie := &http.Cookie{}
|
||||
sess.isNew = len(sess.values) == 0
|
||||
|
||||
// The RFC makes no mention of encoding url value, so here I think to encode both sessionid key and the value using the safe(to put and to use as cookie) url-encoding
|
||||
cookie.Name = s.config.Cookie
|
||||
|
||||
cookie.Value = sid
|
||||
cookie.Path = "/"
|
||||
if !s.config.DisableSubdomainPersistence {
|
||||
|
||||
requestDomain := ctx.Request().URL.Host
|
||||
if portIdx := strings.IndexByte(requestDomain, ':'); portIdx > 0 {
|
||||
requestDomain = requestDomain[0:portIdx]
|
||||
}
|
||||
if IsValidCookieDomain(requestDomain) {
|
||||
|
||||
// RFC2109, we allow level 1 subdomains, but no further
|
||||
// if we have localhost.com , we want the localhost.cos.
|
||||
// so if we have something like: mysubdomain.localhost.com we want the localhost here
|
||||
// if we have mysubsubdomain.mysubdomain.localhost.com we want the .mysubdomain.localhost.com here
|
||||
// slow things here, especially the 'replace' but this is a good and understable( I hope) way to get the be able to set cookies from subdomains & domain with 1-level limit
|
||||
if dotIdx := strings.LastIndexByte(requestDomain, '.'); dotIdx > 0 {
|
||||
// is mysubdomain.localhost.com || mysubsubdomain.mysubdomain.localhost.com
|
||||
s := requestDomain[0:dotIdx] // set mysubdomain.localhost || mysubsubdomain.mysubdomain.localhost
|
||||
if secondDotIdx := strings.LastIndexByte(s, '.'); secondDotIdx > 0 {
|
||||
//is mysubdomain.localhost || mysubsubdomain.mysubdomain.localhost
|
||||
s = s[secondDotIdx+1:] // set to localhost || mysubdomain.localhost
|
||||
}
|
||||
// replace the s with the requestDomain before the domain's siffux
|
||||
subdomainSuff := strings.LastIndexByte(requestDomain, '.')
|
||||
if subdomainSuff > len(s) { // if it is actual exists as subdomain suffix
|
||||
requestDomain = strings.Replace(requestDomain, requestDomain[0:subdomainSuff], s, 1) // set to localhost.com || mysubdomain.localhost.com
|
||||
}
|
||||
}
|
||||
// finally set the .localhost.com (for(1-level) || .mysubdomain.localhost.com (for 2-level subdomain allow)
|
||||
cookie.Domain = "." + requestDomain // . to allow persistence
|
||||
}
|
||||
}
|
||||
|
||||
cookie.HttpOnly = true
|
||||
// MaxAge=0 means no 'Max-Age' attribute specified.
|
||||
// MaxAge<0 means delete cookie now, equivalently 'Max-Age: 0'
|
||||
// MaxAge>0 means Max-Age attribute present and given in seconds
|
||||
if s.config.Expires >= 0 {
|
||||
if s.config.Expires == 0 { // unlimited life
|
||||
cookie.Expires = CookieExpireUnlimited
|
||||
} else { // > 0
|
||||
cookie.Expires = time.Now().Add(s.config.Expires)
|
||||
}
|
||||
cookie.MaxAge = int(cookie.Expires.Sub(time.Now()).Seconds())
|
||||
}
|
||||
|
||||
// set the cookie to secure if this is a tls wrapped request
|
||||
// and the configuration allows it.
|
||||
if ctx.Request().TLS != nil && s.config.CookieSecureTLS {
|
||||
cookie.Secure = true
|
||||
}
|
||||
|
||||
// encode the session id cookie client value right before send it.
|
||||
cookie.Value = s.encodeCookieValue(cookie.Value)
|
||||
AddCookie(ctx, cookie)
|
||||
s.updateCookie(sid, ctx, s.config.Expires)
|
||||
|
||||
return sess
|
||||
}
|
||||
|
||||
cookieValue = s.decodeCookieValue(cookieValue)
|
||||
sess := s.provider.Read(cookieValue, s.config.Expires)
|
||||
return sess
|
||||
|
||||
return sess
|
||||
}
|
||||
|
||||
// ShiftExpiraton move the expire date of a session to a new date by using session default timeout configuration
|
||||
func (s *Sessions) ShiftExpiraton(ctx context.Context) {
|
||||
s.UpdateExpiraton(ctx, s.config.Expires)
|
||||
}
|
||||
|
||||
// UpdateExpiraton change expire date of a session to a new date by using timeout value passed by `expires` parameter
|
||||
func (s *Sessions) UpdateExpiraton(ctx context.Context, expires time.Duration) {
|
||||
cookieValue := GetCookie(ctx, s.config.Cookie)
|
||||
|
||||
if cookieValue != "" {
|
||||
sid := s.decodeCookieValue(cookieValue)
|
||||
if s.provider.UpdateExpiraton(sid, expires) {
|
||||
s.updateCookie(sid, ctx, expires)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Destroy remove the session data and remove the associated cookie.
|
||||
|
|
Loading…
Reference in New Issue
Block a user