From 1b3b3cd7d3e8eb63163343c03ea6a263b08983df Mon Sep 17 00:00:00 2001 From: dxvgef Date: Fri, 10 Jul 2020 17:07:08 +0800 Subject: [PATCH] =?UTF-8?q?-=20=E4=BD=BF=E7=94=A8snowflake=20id=E6=9B=BF?= =?UTF-8?q?=E4=BB=A3uuid=E5=81=9Asession=20id=E7=9A=84=E7=A7=8D=E5=AD=90?= =?UTF-8?q?=E5=80=BC=EF=BC=8C=E6=8F=90=E5=8D=87=E6=80=A7=E8=83=BD=20-=20?= =?UTF-8?q?=E4=BF=AE=E5=A4=8D=E5=9C=A8=E5=90=8C=E4=B8=80=E4=BC=9A=E8=AF=9D?= =?UTF-8?q?=E4=B8=AD=E6=96=B0=E5=BB=BAsession=20id=E5=90=8C=E6=97=B6?= =?UTF-8?q?=E5=86=99=E5=85=A5session=E6=95=B0=E6=8D=AE=E6=97=B6=EF=BC=8Cse?= =?UTF-8?q?ssion=E6=95=B0=E6=8D=AE=E7=9A=84=E7=94=9F=E5=91=BD=E5=91=A8?= =?UTF-8?q?=E6=9C=9F=E5=A4=B1=E6=95=88=E7=9A=84=E9=97=AE=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- engine.go | 78 ++++++++++++++++++++++++++---------------------------- session.go | 36 ++++++++++++++++++++----- 2 files changed, 66 insertions(+), 48 deletions(-) diff --git a/engine.go b/engine.go index d23fb88..5d2e3af 100644 --- a/engine.go +++ b/engine.go @@ -3,16 +3,19 @@ package sessions import ( "encoding/hex" "errors" + "math/rand" "net/http" + "strconv" "time" + "github.com/bwmarrin/snowflake" "github.com/go-redis/redis/v7" - "github.com/google/uuid" ) // Engine session管理引擎 type Engine struct { - config *Config // 配置 + config *Config // 配置 + seedIDNode *snowflake.Node // 种子ID节点 } // RedisError Redis错误 @@ -73,6 +76,13 @@ func NewEngine(config *Config) (*Engine, error) { // 将redis连接对象传入session管理器 engine.config = config + // 创建种子ID节点的实例 + seedIDNode, err := newSeedIDNode() + if err != nil { + return nil, err + } + engine.seedIDNode = seedIDNode + return &engine, nil } @@ -81,8 +91,6 @@ func (this *Engine) Use(req *http.Request, resp http.ResponseWriter) (*Session, var ( sess Session cookieValid = true - sidValue string - sid string ) // 从cookie中获得sessionID @@ -93,26 +101,28 @@ func (this *Engine) Use(req *http.Request, resp http.ResponseWriter) (*Session, // 如果cookie中的sessionID有效 if cookieValid { - // 将cookie中的值解码 - sid, err = decodeSID(cookieObj.Value, this.config.Key) + // 将cookie中的cid解码成sid + sid, err := decodeSID(cookieObj.Value, this.config.Key) if err != nil { return nil, err } - // 将uuid作为sessionID赋值给session对象 - sess.ID = sid + sess.CookieID = cookieObj.Value + sess.StorageID = sid } else { - var err error - // 生成一个uuid并赋值给session对象 - sess.ID = uuid.New().String() - // 将uuid结合key加密成sid - if sidValue, err = encodeByBytes(strToByte(this.config.Key), strToByte(sess.ID)); err != nil { + // 如果cookies中的sessionID无效 + // 生成种子id + seedID := this.seedIDNode.Generate().String() + strconv.FormatUint(uint64(rand.New(rand.NewSource(rand.Int63n(time.Now().UnixNano()))).Uint32()), 10) + // 用种子ID编码成cid + cid, err := encodeByBytes(strToByte(this.config.Key), strToByte(seedID)) + if err != nil { return nil, err } - + sess.CookieID = cid + sess.StorageID = this.config.RedisKeyPrefix + ":" + seedID // 创建一个cookie对象并赋值后写入到客户端 http.SetCookie(resp, &http.Cookie{ Name: this.config.CookieName, - Value: sidValue, + Value: cid, Domain: this.config.Domain, Path: this.config.Path, Expires: time.Now().Add(this.config.IdleTime), @@ -122,34 +132,18 @@ func (this *Engine) Use(req *http.Request, resp http.ResponseWriter) (*Session, }) } - sess.ID = this.config.RedisKeyPrefix + ":" + sess.ID - sess.req = req sess.resp = resp - - // 自动更新空闲时间 - if !this.config.DisableAutoUpdateIdleTime { - if err := this.UpdateIdleTime(req, resp); err != nil { - return nil, err - } - } + sess.engine = this return &sess, nil } // 更新session的空闲时间 -func (this *Engine) UpdateIdleTime(req *http.Request, resp http.ResponseWriter) error { - // 从cookie中获得sessionID - cookieObj, err := req.Cookie(this.config.CookieName) - if err != nil || cookieObj == nil { - return nil - } else if cookieObj.Value == "" { - return nil - } - +func (this *Engine) UpdateIdleTime(cid, sid string, resp http.ResponseWriter) error { // 更新cookie的超时时间 http.SetCookie(resp, &http.Cookie{ Name: this.config.CookieName, - Value: cookieObj.Value, + Value: cid, Domain: this.config.Domain, Path: this.config.Path, Expires: time.Now().Add(this.config.IdleTime), @@ -158,13 +152,7 @@ func (this *Engine) UpdateIdleTime(req *http.Request, resp http.ResponseWriter) HttpOnly: this.config.HttpOnly, }) - // 将cookie中的值解码 - sid, err := decodeSID(cookieObj.Value, this.config.Key) - if err != nil { - return err - } - // 更新redis的超时时间 - return redisClient.ExpireAt(this.config.RedisKeyPrefix+":"+sid, time.Now().Add(this.config.IdleTime)).Err() + return redisClient.ExpireAt(sid, time.Now().Add(this.config.IdleTime)).Err() } // 解码得到sessionID @@ -297,3 +285,11 @@ func (engine *Engine) DeleteByID(id, key string) error { } return redisClient.HDel(sid, key).Err() } + +// 设置种子ID的实例 +func newSeedIDNode() (*snowflake.Node, error) { + snowflake.Epoch = time.Now().Unix() + rand.Seed(rand.Int63n(time.Now().UnixNano())) + node := 0 + rand.Int63n(1023-0) + return snowflake.NewNode(node) +} diff --git a/session.go b/session.go index 07526ec..73d67d3 100644 --- a/session.go +++ b/session.go @@ -9,9 +9,10 @@ import ( // session对象 type Session struct { - ID string - req *http.Request - resp http.ResponseWriter + StorageID string // 存储器中的ID + CookieID string // cookies中的ID + resp http.ResponseWriter + engine *Engine } // 键不存在时的错误类型 @@ -29,18 +30,39 @@ type Value struct { func (obj *Session) Get(key string) *Value { var result Value result.Key = key - value, err := redisClient.HGet(obj.ID, key).Result() + + value, err := redisClient.HGet(obj.StorageID, key).Result() if err != nil { result.Error = err return &result } result.Value = value + + // 自动更新空闲时间 + if !obj.engine.config.DisableAutoUpdateIdleTime { + if err := obj.engine.UpdateIdleTime(obj.CookieID, obj.StorageID, obj.resp); err != nil { + result.Error = err + return &result + } + } + return &result } // 设置一个键值,如果键名存在则覆盖 func (obj *Session) Set(key string, value interface{}) error { - return redisClient.HSet(obj.ID, key, value).Err() + err := redisClient.HSet(obj.StorageID, key, value).Err() + if err != nil { + return err + } + // 自动更新空闲时间 + if !obj.engine.config.DisableAutoUpdateIdleTime { + if err := obj.engine.UpdateIdleTime(obj.CookieID, obj.StorageID, obj.resp); err != nil { + return err + } + } + + return nil } // String 将值转为string类型 @@ -205,10 +227,10 @@ func (v *Value) Bool(def ...bool) (bool, error) { // Delete 删除一个键值,如果键名不存在则忽略,不会报错 func (this *Session) Delete(key string) error { - return redisClient.HDel(this.ID, key).Err() + return redisClient.HDel(this.StorageID, key).Err() } // ClearData 清除所有redis中的session数据,但不删除cookie中的sessionID func (this *Session) ClearData() error { - return redisClient.Del(this.ID).Err() + return redisClient.Del(this.StorageID).Err() }