github.com/qiuhoude/go-web@v0.0.0-20220223060959-ab545e78f20d/prepare/05_session/session/session.go (about)

     1  package session
     2  
     3  import (
     4  	"crypto/rand"
     5  	"encoding/base64"
     6  	"fmt"
     7  	"io"
     8  	"net/http"
     9  	"net/url"
    10  	"sync"
    11  	"time"
    12  )
    13  
    14  var provides = make(map[string]Provider)
    15  
    16  type (
    17  	Provider interface {
    18  		SessionInit(sid string) (Session, error)
    19  		SessionRead(sid string) (Session, error)
    20  		SessionDestroy(sid string) error
    21  		SessionGC(maxLifeTime int64)
    22  	}
    23  
    24  	Session interface {
    25  		Set(key, value interface{}) error
    26  		Get(key interface{}) interface{}
    27  		Delete(key interface{}) error
    28  		SessionID() string
    29  	}
    30  )
    31  
    32  type (
    33  	Manager struct {
    34  		cookieName  string
    35  		lock        sync.Mutex
    36  		maxLifeTime int64 // 最大过期时间
    37  		provider    Provider
    38  	}
    39  )
    40  
    41  func NewManager(provideName, cookieName string, maxLifeTime int64) (*Manager, error) {
    42  	provider, ok := provides[provideName]
    43  	if !ok {
    44  		return nil, fmt.Errorf("session: unknown provide %q (forgotten import?)", provideName)
    45  	}
    46  	return &Manager{
    47  		cookieName:  cookieName,
    48  		maxLifeTime: maxLifeTime,
    49  		provider:    provider,
    50  	}, nil
    51  }
    52  
    53  // 生成sessionId
    54  func (m *Manager) sessionId() string {
    55  	b := make([]byte, 32)
    56  	if _, err := io.ReadFull(rand.Reader, b); err != nil {
    57  		return ""
    58  	}
    59  	return base64.URLEncoding.EncodeToString(b)
    60  }
    61  
    62  // 创建session
    63  func (m *Manager) SessionStart(rw http.ResponseWriter, req *http.Request) Session {
    64  	m.lock.Lock()
    65  	defer m.lock.Unlock()
    66  	cookie, err := req.Cookie(m.cookieName)
    67  	if err != nil || cookie.Value == "" { // 获取出错,或者值为nil就重新设置cookie
    68  		sid := m.sessionId()
    69  		cookie := http.Cookie{
    70  			Name:  m.cookieName,
    71  			Value: url.QueryEscape(sid),
    72  			Path:  "/",
    73  			//MaxAge:   int(m.maxLifeTime),
    74  			HttpOnly: true, //这个属性是设置是否可通过客户端脚本访问这个设置的cookie
    75  		}
    76  		http.SetCookie(rw, &cookie)
    77  		session, _ := m.provider.SessionInit(sid)
    78  		return session
    79  	} else {
    80  		sid, _ := url.QueryUnescape(cookie.Value)
    81  		session, _ := m.provider.SessionRead(sid)
    82  		return session
    83  	}
    84  }
    85  
    86  //重置session
    87  func (m *Manager) SessionDestroy(rw http.ResponseWriter, req *http.Request) {
    88  	cookie, err := req.Cookie(m.cookieName)
    89  	if err != nil || cookie.Value == "" {
    90  		return
    91  	} else {
    92  		m.lock.Lock()
    93  		defer m.lock.Unlock()
    94  		m.provider.SessionDestroy(cookie.Value)
    95  		expiration := time.Now()
    96  		cookie := http.Cookie{
    97  			Name:     m.cookieName,
    98  			Path:     "/",
    99  			HttpOnly: true,
   100  			Expires:  expiration,
   101  		}
   102  		http.SetCookie(rw, &cookie)
   103  	}
   104  }
   105  
   106  func (m *Manager) CookieName() string {
   107  	return m.cookieName
   108  }
   109  
   110  // 过期回收,每间隔 maxLifeTime 时间就来清理一次session
   111  func (m *Manager) GC() {
   112  	m.lock.Lock()
   113  	defer m.lock.Unlock()
   114  	m.provider.SessionGC(m.maxLifeTime)
   115  	time.AfterFunc(time.Duration(m.maxLifeTime)*time.Second, func() {
   116  		m.GC()
   117  	})
   118  }
   119  
   120  //注册provide, 不能重复注册,在调用的地方 init中执行
   121  func Register(name string, provide Provider) {
   122  	if provide == nil {
   123  		panic("session: Register provide is nil")
   124  	}
   125  	if _, dup := provides[name]; dup {
   126  		panic("session: Register called twice for provide " + name)
   127  	}
   128  	provides[name] = provide
   129  }