2018-05-31 21:24:59 +00:00
|
|
|
package session
|
|
|
|
|
|
|
|
import (
|
|
|
|
"crypto/rand"
|
|
|
|
"encoding/base64"
|
|
|
|
"net/http"
|
|
|
|
"sync"
|
|
|
|
"time"
|
|
|
|
)
|
|
|
|
|
|
|
|
var (
|
|
|
|
CookieName = "session"
|
|
|
|
|
|
|
|
Expiration = time.Hour * 24 * 7
|
|
|
|
RefreshInterval = time.Hour
|
|
|
|
)
|
|
|
|
|
|
|
|
type Session struct {
|
|
|
|
UserID uint64
|
|
|
|
|
2018-06-01 03:59:13 +00:00
|
|
|
key string
|
|
|
|
createdAt int64
|
|
|
|
lock sync.Mutex
|
2018-05-31 21:24:59 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
func New(id uint64) (*Session, error) {
|
|
|
|
key, err := newSessionKey()
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
return &Session{
|
2018-06-01 03:59:13 +00:00
|
|
|
key: key,
|
|
|
|
createdAt: time.Now().Unix(),
|
|
|
|
UserID: id,
|
2018-05-31 21:24:59 +00:00
|
|
|
}, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func (s *Session) Key() string {
|
|
|
|
s.lock.Lock()
|
|
|
|
key := s.key
|
|
|
|
s.lock.Unlock()
|
|
|
|
return key
|
|
|
|
}
|
|
|
|
|
|
|
|
func (s *Session) SetCookie(w http.ResponseWriter, r *http.Request) {
|
2018-06-01 03:59:13 +00:00
|
|
|
s.lock.Lock()
|
|
|
|
created := time.Unix(s.createdAt, 0)
|
|
|
|
s.lock.Unlock()
|
|
|
|
|
2018-10-06 06:56:29 +00:00
|
|
|
cookie := &http.Cookie{
|
2018-05-31 21:24:59 +00:00
|
|
|
Name: CookieName,
|
|
|
|
Value: s.Key(),
|
|
|
|
Path: "/",
|
2018-06-01 03:59:13 +00:00
|
|
|
Expires: created.Add(Expiration),
|
2018-05-31 21:24:59 +00:00
|
|
|
HttpOnly: true,
|
|
|
|
Secure: r.TLS != nil,
|
2018-10-06 06:56:29 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
if v := cookie.String(); v != "" {
|
2018-10-19 00:11:12 +00:00
|
|
|
w.Header().Add("Set-Cookie", v+"; SameSite=Lax")
|
2018-10-06 06:56:29 +00:00
|
|
|
}
|
2018-05-31 21:24:59 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
func (s *Session) Expired() bool {
|
|
|
|
s.lock.Lock()
|
|
|
|
created := time.Unix(s.createdAt, 0)
|
|
|
|
s.lock.Unlock()
|
|
|
|
return time.Since(created) > Expiration
|
|
|
|
}
|
|
|
|
|
|
|
|
func (s *Session) Refresh() (string, bool, error) {
|
|
|
|
s.lock.Lock()
|
|
|
|
created := time.Unix(s.createdAt, 0)
|
|
|
|
s.lock.Unlock()
|
|
|
|
|
|
|
|
if time.Since(created) > Expiration {
|
|
|
|
return "", true, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
if time.Since(created) > RefreshInterval {
|
|
|
|
key, err := newSessionKey()
|
|
|
|
if err != nil {
|
|
|
|
return "", false, err
|
|
|
|
}
|
|
|
|
|
|
|
|
s.lock.Lock()
|
|
|
|
s.createdAt = time.Now().Unix()
|
|
|
|
s.key = key
|
|
|
|
s.lock.Unlock()
|
|
|
|
return key, false, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
return "", false, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func newSessionKey() (string, error) {
|
|
|
|
key := make([]byte, 32)
|
|
|
|
_, err := rand.Read(key)
|
|
|
|
return base64.RawURLEncoding.EncodeToString(key), err
|
|
|
|
}
|