Go 尝试实现session管理

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
package session
 
import (
    "fmt"
    "github.com/satori/go.uuid"
    "log"
    "net/http"
    "net/url"
    "sync"
    "time"
)
 
const (
    CookieName = "goodsCool"
    Timeout    = 60 * 30
)
 
var Cmgr *ConversationManager
 
func init() {
    Cmgr = &ConversationManager{
        CookieName: CookieName,
        MaxAge:     Timeout,
        Storage:    MemStorage{DataStorage: make(map[string]interface{}, 0)},
    }
    go Cmgr.GC()
}
 
type MemStorage struct {
    DataStorage   map[string]interface{}
    Lock          sync.Mutex
    ReadWriteLock sync.RWMutex
}
 
func (ms *MemStorage) Store(key string, value interface{}) bool {
    ms.Lock.Lock()
    storage := ms.DataStorage
    storage[key] = value
    ms.Lock.Unlock()
    return true
}
func (ms *MemStorage) Load(key string) (value interface{}, ok bool) {
    ms.ReadWriteLock.RLock()
    storage := ms.DataStorage
    value = storage[key]
    if value != nil {
        ok = true
    } else {
        ok = false
    }
 
    ms.ReadWriteLock.RUnlock()
    return
}
func (ms *MemStorage) Delete(key string) (value interface{}) {
    ms.Lock.Lock()
    storage := ms.DataStorage
    if v, ok := storage[key]; ok {
        value = v
    }
    delete(storage, key)
    ms.Lock.Unlock()
    return
}
 
func (ms *MemStorage) Range() {
    for key, value := range ms.DataStorage {
        fmt.Printf(" 遍历 %v,%v\n", key, value)
    }
}
 
type IConversationManager interface {
    Create(cid string) (conversation *Conversation, err error)
    Destroy(cid string) (b bool, err error)
    GC()
}
 
type IConversation interface {
    Get(key interface{}) (info interface{}, err error)
    Set(key interface{}, value interface{}) (b bool, err error)
}
 
type Conversation struct {
    LastAccessTime time.Time
    Cid            string
    CSData         MemStorage
    MaxAge         int64
}
type ConversationManager struct {
    CookieName string
    MaxAge     int64
    Storage    MemStorage
}
 
//实现 Create / Distroy / Get / Set 方法
//创建会话
func (cm *ConversationManager) Create(cid string) (*Conversation, error) {
    //根据cid 创建一个conversatioin
    storage := cm.Storage
    value, ok := storage.Load(cid)
    if ok {
        conversation := value.(*Conversation)
        return conversation, nil
    }
 
    m := MemStorage{DataStorage: make(map[string]interface{}, 0)}
    c2 := &Conversation{
        LastAccessTime: time.Now(),
        Cid:            cid,
        CSData:         m,
        MaxAge:         60 * 30,
    }
 
    cm.Storage.Store(cid, c2)
    return c2, nil
}
 
//getConversation
func (cm *ConversationManager) GetConversation(cid string) (conversation *Conversation) {
    storage := cm.Storage
    value, ok := storage.Load(cid)
    if ok {
        conversation = value.(*Conversation)
        return conversation
    }
    return conversation
}
 
// 销毁cid 对应的会话
func (cm *ConversationManager) Destroy(cid string) (b bool, err error) {
    if _, ok := cm.Storage.Load(cid); ok {
        cm.Storage.Delete(cid)
    }
    return true, nil
}
 
func (cm *ConversationManager) GetConversationData(r *http.Request) (mem *MemStorage) {
    cookie, _ := r.Cookie(CookieName)
    if cookie != nil {
        conversationId := cookie.Value
        conversation := Cmgr.GetConversation(conversationId)
        return &(conversation.CSData)
    } else {
        return nil
    }
}
 
// 会话域存储Get
func (c *Conversation) Get(key string) (interface{}, error) {
    //log.Println("从", c.Cid, "中取数据:key=", key)
    if load, ok := c.CSData.Load(key); ok {
        return load, nil
    } else {
        return nil, nil
    }
}
 
// 会话域存储Set
func (c *Conversation) Set(key string, value interface{}) (b bool, err error) {
    c.CSData.Store(key, value)
    return true, nil
}
 
// ConversationManager GC
func (cm *ConversationManager) GC() {
    for {
        time.Sleep(time.Second * 10)
        log.Println("GC ing ...")
        storage := cm.Storage.DataStorage
        duration := time.Duration(cm.MaxAge * 1000 * 1000 * 1000)
        now := time.Now()
        for cid, conversation := range storage {
            //log.Printf("GC 遍历:%v,%v\n", cid, conversation)
            if c, ok := conversation.(*Conversation); ok {
                expire := c.LastAccessTime.Add(duration)
                //log.Println("如果 expire 大于当前时间:", expire, " 当前时间:now=", now)
                if now.After(expire) {
                    //将conversation销毁
                    log.Println("销毁Conversation: Cid=", cid)
                    cm.Destroy(cid)
                }
            }
        }
    }
}
 
// 给普通链接处理会话,没有就创建同时处理客户端
//的cookie
func (cm *ConversationManager) ManagerNormalRequest(w http.ResponseWriter, r *http.Request) (conversation *Conversation, e error) {
    cookie, err := r.Cookie(CookieName)
    if err != nil {
        e = err
    }
    var conversationId string
    if cookie == nil {
        conversationId, err = url.QueryUnescape(uuid.Must(uuid.NewV4(), nil).String())
        if err != nil {
            e = err
            return
        }
    } else {
        conversationId = cookie.Value
    }
    //创建Conversation,如果有就返回
    cs, err := cm.Create(conversationId)
    if err != nil {
        e = err
        return
    }
    cs.LastAccessTime = time.Now()
    expire := cs.LastAccessTime.Add(time.Duration(cm.MaxAge * 1000 * 1000 * 1000))
    //log.Println("expire: ", expire)
    //设置cookie
    c := http.Cookie{
        Name:  CookieName,
        Value: conversationId,
        Path:  "/",
        //Domain:     "",
        Expires:    expire,
        RawExpires: "",
        MaxAge:     int(cm.MaxAge),
        Secure:     false,
        HttpOnly:   false,
        SameSite:   0,
        Raw:        "",
        Unparsed:   nil,
    }
    //log.Println(c)
    http.SetCookie(w, &c)
    return cs, e
}
 
func UUID() (string, error) {
    conversationId, err := url.QueryUnescape(uuid.Must(uuid.NewV4(), nil).String())
    return conversationId, err
}
 
func (cm *ConversationManager) ManagerLogin(loginUser interface{}, w http.ResponseWriter, r *http.Request) {
    cookie, _ := r.Cookie(CookieName)
    //获取当前时间
    now := time.Now()
    var conversationId string
    if cookie != nil {
        //log.Println("已经有了cookie ==>", cookie)
        conversationId = cookie.Value
    } else {
        conversationId, _ = UUID()
        //log.Println("没有cookie,创建会话 cid=", conversationId)
    }
    expire := now.Add(time.Duration(Cmgr.MaxAge * 1000 * 1000 * 1000))
    //设置cookie
    cookie2 := http.Cookie{
        Name:  CookieName,
        Value: conversationId,
        Path:  "/",
        //  Domain:     "",
        Expires:    expire,
        RawExpires: "",
        MaxAge:     0,
        Secure:     false,
        HttpOnly:   false,
        SameSite:   0,
        Raw:        "",
        Unparsed:   nil,
    }
    //存放登录数据
    var conversation *Conversation
    conversation = Cmgr.GetConversation(conversationId)
    if conversation == nil {
        create, _ := Cmgr.Create(cookie2.Value)
        conversation = create
    }
    conversation.LastAccessTime = now
    conversation.Set("user", loginUser)
    log.Println("存放了User: conversationId:", conversation.Cid, "存放的用户:", loginUser)
    http.SetCookie(w, &cookie2)
}

  使用非常简单,添加Gin 中间件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
func ConversationManager() gin.HandlerFunc {
    return func(c *gin.Context) {
        url := c.Request.URL
        //  log.Println("当前访问链接:", url.String())
        if url.Path == "/login" {
            //如果是登录链接,就放行
            c.Next()
            session.Cmgr.ManagerNormalRequest(c.Writer, c.Request)
            return
        }
      
        //判断有没有cookie ,若有cookie 就根据ConversationId寻找后台对应的Conversation
        //判断Conversation中有没有登录用户
        cookie, _ := c.Request.Cookie(session.CookieName)
        if cookie != nil {
            conversation := session.Cmgr.GetConversation(cookie.Value)
            if conversation != nil {
                login, _ := conversation.Get("user")
                if login == nil {
                    session.Cmgr.ManagerNormalRequest(c.Writer, c.Request)
                    c.HTML(200, "login.tmpl", nil)
                } else {
                    //user := login.(*models.User)
                    //log.Println("conversationId:", conversation.Cid, "  用户: ", user.ToString())
                    c.Next()
                }
            } else {
                session.Cmgr.ManagerNormalRequest(c.Writer, c.Request)
                c.HTML(200, "login.tmpl", nil)
            }
        } else {
            session.Cmgr.ManagerNormalRequest(c.Writer, c.Request)
            c.HTML(200, "login.tmpl", nil)
        }
 
    }
}

  

1
2
3
4
5
6
7
8
9
10
11
12
13
func main() {
  
    router := gin.Default()
  
    //router.Use(middleware.Cors())
    router.StaticFS("/static", http.Dir("src/static"))
    router.StaticFS("/image/user", http.Dir(imagePath))
    router.LoadHTMLGlob("src/views/admin/*")
    router.NoRoute(controllers.Handle404)
 
    router.Use(middleware.ConversationManager())
    router.GET("/", controllers.Index)
}

  

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
func LoginAction(c *gin.Context) {
    userName := c.PostForm("username")
    passWord := c.PostForm("password")
 
    u := &models.User{UserName: userName, PassWord: passWord}
    login, err := u.Login(u)
    if err != nil {
        log.Println(err)
    }
    if login == nil {
        c.HTML(http.StatusOK, "login.tmpl", nil)
    } else {
        session.Cmgr.ManagerLogin(login, c.Writer, c.Request)
        data := make(map[string]interface{})
        data["loginUser"] = login
        c.HTML(http.StatusOK, "index.tmpl", data)
    }
}

  

posted @   和平鸽  阅读(702)  评论(0编辑  收藏  举报
编辑推荐:
· .NET Core 托管堆内存泄露/CPU异常的常见思路
· PostgreSQL 和 SQL Server 在统计信息维护中的关键差异
· C++代码改造为UTF-8编码问题的总结
· DeepSeek 解答了困扰我五年的技术问题
· 为什么说在企业级应用开发中,后端往往是效率杀手?
阅读排行:
· 10亿数据,如何做迁移?
· 推荐几款开源且免费的 .NET MAUI 组件库
· 清华大学推出第四讲使用 DeepSeek + DeepResearch 让科研像聊天一样简单!
· c# 半导体/led行业 晶圆片WaferMap实现 map图实现入门篇
· 易语言 —— 开山篇
点击右上角即可分享
微信分享提示