github.com/cloudreve/Cloudreve/v3@v3.0.0-20240224133659-3edb00a6484c/pkg/sessionstore/kv.go (about) 1 package sessionstore 2 3 import ( 4 "bytes" 5 "encoding/base32" 6 "encoding/gob" 7 "github.com/cloudreve/Cloudreve/v3/pkg/cache" 8 "github.com/gorilla/securecookie" 9 "github.com/gorilla/sessions" 10 "net/http" 11 "strings" 12 ) 13 14 type kvStore struct { 15 Codecs []securecookie.Codec 16 Options *sessions.Options 17 DefaultMaxAge int 18 19 prefix string 20 serializer SessionSerializer 21 store cache.Driver 22 } 23 24 func newKvStore(prefix string, store cache.Driver, keyPairs ...[]byte) *kvStore { 25 return &kvStore{ 26 prefix: prefix, 27 store: store, 28 DefaultMaxAge: 60 * 20, 29 serializer: GobSerializer{}, 30 Codecs: securecookie.CodecsFromPairs(keyPairs...), 31 Options: &sessions.Options{ 32 Path: "/", 33 MaxAge: 86400 * 30, 34 }, 35 } 36 } 37 38 // Get returns a session for the given name after adding it to the registry. 39 // 40 // It returns a new session if the sessions doesn't exist. Access IsNew on 41 // the session to check if it is an existing session or a new one. 42 // 43 // It returns a new session and an error if the session exists but could 44 // not be decoded. 45 func (s *kvStore) Get(r *http.Request, name string) (*sessions.Session, error) { 46 return sessions.GetRegistry(r).Get(s, name) 47 } 48 49 // New returns a session for the given name without adding it to the registry. 50 // 51 // The difference between New() and Get() is that calling New() twice will 52 // decode the session data twice, while Get() registers and reuses the same 53 // decoded session after the first call. 54 func (s *kvStore) New(r *http.Request, name string) (*sessions.Session, error) { 55 var ( 56 err error 57 ) 58 session := sessions.NewSession(s, name) 59 // make a copy 60 options := *s.Options 61 session.Options = &options 62 session.IsNew = true 63 if c, errCookie := r.Cookie(name); errCookie == nil { 64 err = securecookie.DecodeMulti(name, c.Value, &session.ID, s.Codecs...) 65 if err == nil { 66 res, ok := s.store.Get(s.prefix + session.ID) 67 if ok { 68 err = s.serializer.Deserialize(res.([]byte), session) 69 } 70 71 session.IsNew = !(err == nil && ok) // not new if no error and data available 72 } 73 } 74 return session, err 75 } 76 func (s *kvStore) Save(r *http.Request, w http.ResponseWriter, session *sessions.Session) error { 77 // Marked for deletion. 78 if session.Options.MaxAge <= 0 { 79 if err := s.store.Delete([]string{session.ID}, s.prefix); err != nil { 80 return err 81 } 82 http.SetCookie(w, sessions.NewCookie(session.Name(), "", session.Options)) 83 } else { 84 // Build an alphanumeric key for the redis store. 85 if session.ID == "" { 86 session.ID = strings.TrimRight(base32.StdEncoding.EncodeToString(securecookie.GenerateRandomKey(32)), "=") 87 } 88 89 b, err := s.serializer.Serialize(session) 90 if err != nil { 91 return err 92 } 93 94 age := session.Options.MaxAge 95 if age == 0 { 96 age = s.DefaultMaxAge 97 } 98 99 if err := s.store.Set(s.prefix+session.ID, b, age); err != nil { 100 return err 101 } 102 103 encoded, err := securecookie.EncodeMulti(session.Name(), session.ID, s.Codecs...) 104 if err != nil { 105 return err 106 } 107 http.SetCookie(w, sessions.NewCookie(session.Name(), encoded, session.Options)) 108 } 109 return nil 110 } 111 112 // SessionSerializer provides an interface hook for alternative serializers 113 type SessionSerializer interface { 114 Deserialize(d []byte, ss *sessions.Session) error 115 Serialize(ss *sessions.Session) ([]byte, error) 116 } 117 118 // GobSerializer uses gob package to encode the session map 119 type GobSerializer struct{} 120 121 // Serialize using gob 122 func (s GobSerializer) Serialize(ss *sessions.Session) ([]byte, error) { 123 buf := new(bytes.Buffer) 124 enc := gob.NewEncoder(buf) 125 err := enc.Encode(ss.Values) 126 if err == nil { 127 return buf.Bytes(), nil 128 } 129 return nil, err 130 } 131 132 // Deserialize back to map[interface{}]interface{} 133 func (s GobSerializer) Deserialize(d []byte, ss *sessions.Session) error { 134 dec := gob.NewDecoder(bytes.NewBuffer(d)) 135 return dec.Decode(&ss.Values) 136 }